| // Copyright 2022 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import * as i18n from '../../../../core/i18n/i18n.js'; |
| import {assertNotNullOrUndefined} from '../../../../core/platform/platform.js'; |
| import * as SDK from '../../../../core/sdk/sdk.js'; |
| import * as Protocol from '../../../../generated/protocol.js'; |
| import * as ComponentHelpers from '../../../../ui/components/helpers/helpers.js'; |
| import * as Coordinator from '../../../../ui/components/render_coordinator/render_coordinator.js'; |
| import * as ReportView from '../../../../ui/components/report_view/report_view.js'; |
| import * as LitHtml from '../../../../ui/lit-html/lit-html.js'; |
| |
| import preloadingDetailsReportViewStyles from './preloadingDetailsReportView.css.js'; |
| import {PrefetchReasonDescription} from './PreloadingString.js'; |
| |
| const UIStrings = { |
| /** |
| *@description Text in PreloadingDetailsReportView of the Application panel |
| */ |
| selectAnElementForMoreDetails: 'Select an element for more details', |
| /** |
| *@description Text in details |
| */ |
| detailsDetailedInformation: 'Detailed information', |
| /** |
| *@description Text in details |
| */ |
| detailsAction: 'Action', |
| /** |
| *@description Text in details |
| */ |
| detailsStatus: 'Status', |
| /** |
| *@description Text in details |
| */ |
| detailsFailureReason: 'Failure reason', |
| /** |
| *@description Header of rule set |
| */ |
| detailsRuleSet: 'Rule set', |
| /** |
| *@description Description: status |
| */ |
| detailedStatusNotTriggered: 'Preloading attempt is not yet triggered.', |
| /** |
| *@description Description: status |
| */ |
| detailedStatusPending: 'Preloading attempt is eligible but pending.', |
| /** |
| *@description Description: status |
| */ |
| detailedStatusRunning: 'Preloading is running.', |
| /** |
| *@description Description: status |
| */ |
| detailedStatusReady: 'Preloading finished and the result is ready for the next navigation.', |
| /** |
| *@description Description: status |
| */ |
| detailedStatusSuccess: 'Preloading finished and used for a navigation.', |
| /** |
| *@description Description: status |
| */ |
| detailedStatusFailure: 'Preloading failed.', |
| /** |
| * Description text for PrerenderFinalStatus::kLowEndDevice. |
| */ |
| prerenderFinalStatusLowEndDevice: |
| 'The prerender was not performed because this device does not have enough total system memory to support prerendering.', |
| /** |
| * Description text for PrerenderFinalStatus::kInvalidSchemeRedirect. |
| */ |
| prerenderFinalStatusInvalidSchemeRedirect: |
| 'The prerendering navigation failed because it redirected to a URL whose scheme was not http: or https:.', |
| /** |
| * Description text for PrerenderFinalStatus::kInvalidSchemeNavigation. |
| */ |
| prerenderFinalStatusInvalidSchemeNavigation: |
| 'The URL was not eligible to be prerendered because its scheme was not http: or https:.', |
| /** |
| * Description text for PrerenderFinalStatus::kNavigationRequestBlockedByCsp. |
| */ |
| prerenderFinalStatusNavigationRequestBlockedByCsp: |
| 'The prerendering navigation was blocked by a Content Security Policy.', |
| /** |
| * Description text for PrerenderFinalStatus::kMainFrameNavigation. |
| */ |
| prerenderFinalStatusMainFrameNavigation: |
| 'The prerendered page navigated itself to another URL, which is currently not supported.', |
| /** |
| * Description text for PrerenderFinalStatus::kMojoBinderPolicy. |
| */ |
| prerenderFinalStatusMojoBinderPolicy: |
| 'The prerendered page used a forbidden JavaScript API that is currently not supported.', |
| /** |
| * Description text for PrerenderFinalStatus::kRendererProcessCrashed. |
| */ |
| prerenderFinalStatusRendererProcessCrashed: 'The prerendered page crashed.', |
| /** |
| * Description text for PrerenderFinalStatus::kRendererProcessKilled. |
| */ |
| prerenderFinalStatusRendererProcessKilled: 'The prerendered page was killed.', |
| /** |
| * Description text for PrerenderFinalStatus::kDownload. |
| */ |
| prerenderFinalStatusDownload: |
| 'The prerendered page attempted to initiate a download, which is currently not supported.', |
| /** |
| * Description text for PrerenderFinalStatus::kNavigationBadHttpStatus. |
| */ |
| prerenderFinalStatusNavigationBadHttpStatus: |
| 'The prerendering navigation failed because of a non-2xx HTTP response status code.', |
| /** |
| * Description text for PrerenderFinalStatus::kClientCertRequested. |
| */ |
| prerenderFinalStatusClientCertRequested: 'The prerendering navigation required a HTTP client certificate.', |
| /** |
| * Description text for PrerenderFinalStatus::kNavigationRequestNetworkError. |
| */ |
| prerenderFinalStatusNavigationRequestNetworkError: 'The prerendering navigation encountered a network error.', |
| /** |
| * Description text for PrerenderFinalStatus::kMaxNumOfRunningPrerendersExceeded. |
| */ |
| prerenderFinalStatusMaxNumOfRunningPrerendersExceeded: |
| 'The prerender was not performed because the initiating page already has too many prerenders ongoing. Remove other speculation rules to enable further prerendering.', |
| /** |
| * Description text for PrerenderFinalStatus::kSslCertificateError. |
| */ |
| prerenderFinalStatusSslCertificateError: 'The prerendering navigation failed because of an invalid SSL certificate.', |
| /** |
| * Description text for PrerenderFinalStatus::kLoginAuthRequested. |
| */ |
| prerenderFinalStatusLoginAuthRequested: |
| 'The prerendering navigation required HTTP authentication, which is currently not supported.', |
| /** |
| * Description text for PrerenderFinalStatus::kUaChangeRequiresReload. |
| */ |
| prerenderFinalStatusUaChangeRequiresReload: 'Changing User Agent occured in prerendering navigation.', |
| /** |
| * Description text for PrerenderFinalStatus::kBlockedByClient. |
| */ |
| prerenderFinalStatusBlockedByClient: 'Some resource load was blocked.', |
| /** |
| * Description text for PrerenderFinalStatus::kAudioOutputDeviceRequested. |
| */ |
| prerenderFinalStatusAudioOutputDeviceRequested: |
| 'The prerendered page requested audio output, which is currently not supported.', |
| /** |
| * Description text for PrerenderFinalStatus::kMixedContent. |
| */ |
| prerenderFinalStatusMixedContent: 'The prerendered page contained mixed content.', |
| /** |
| * Description text for PrerenderFinalStatus::kTriggerBackgrounded. |
| */ |
| prerenderFinalStatusTriggerBackgrounded: |
| 'The initiating page was backgrounded, so the prerendered page was discarded.', |
| /** |
| * Description text for PrerenderFinalStatus::kMemoryLimitExceeded. |
| */ |
| prerenderFinalStatusMemoryLimitExceeded: |
| 'The prerender was not performed because the browser exceeded the prerendering memory limit.', |
| /** |
| * Description text for PrerenderFinalStatus::kFailToGetMemoryUsage. |
| */ |
| prerenderFinalStatusFailToGetMemoryUsage: |
| 'The prerender was not performed because the browser encountered an internal error attempting to determine current memory usage.', |
| /** |
| * Description text for PrerenderFinalStatus::kDataSaverEnabled. |
| */ |
| prerenderFinalStatusDataSaverEnabled: |
| 'The prerender was not performed because the user requested that the browser use less data.', |
| /** |
| * Description text for PrerenderFinalStatus::kHasEffectiveUrl. |
| */ |
| prerenderFinalStatusHasEffectiveUrl: |
| 'The initiating page cannot perform prerendering, because it has an effective URL that is different from its normal URL. (For example, the New Tab Page, or hosted apps.)', |
| /** |
| * Description text for PrerenderFinalStatus::kTimeoutBackgrounded. |
| */ |
| prerenderFinalStatusTimeoutBackgrounded: |
| 'The initiating page was backgrounded for a long time, so the prerendered page was discarded.', |
| /** |
| * Description text for PrerenderFinalStatus::kCrossSiteRedirectInInitialNavigation. |
| */ |
| prerenderFinalStatusCrossSiteRedirectInInitialNavigation: |
| 'The prerendering navigation failed because the prerendered URL redirected to a cross-site URL.', |
| /** |
| * Description text for PrerenderFinalStatus::kCrossSiteNavigationInInitialNavigation. |
| */ |
| prerenderFinalStatusCrossSiteNavigationInInitialNavigation: |
| 'The prerendering navigation failed because it targeted a cross-site URL.', |
| /** |
| * Description text for PrerenderFinalStatus::kSameSiteCrossOriginRedirectNotOptInInInitialNavigation. |
| */ |
| prerenderFinalStatusSameSiteCrossOriginRedirectNotOptInInInitialNavigation: |
| 'The prerendering navigation failed because the prerendered URL redirected to a cross-origin same-site URL, but the destination response did not include the appropriate Supports-Loading-Mode header.', |
| /** |
| * Description text for PrerenderFinalStatus::kSameSiteCrossOriginNavigationNotOptInInInitialNavigation. |
| */ |
| prerenderFinalStatusSameSiteCrossOriginNavigationNotOptInInInitialNavigation: |
| 'The prerendered page navigated itself to a cross-origin same-site URL, but the destination response did not include the appropriate Supports-Loading-Mode header.', |
| /** |
| * Description text for PrerenderFinalStatus::kActivationNavigationParameterMismatch. |
| */ |
| prerenderFinalStatusActivationNavigationParameterMismatch: |
| 'The prerender was not used because during activation time, different navigation parameters (e.g., HTTP headers) were calculated than during the original prerendering navigation request.', |
| /** |
| * Description text for PrerenderFinalStatus::kPrimaryMainFrameRendererProcessCrashed. |
| */ |
| prerenderFinalStatusPrimaryMainFrameRendererProcessCrashed: 'The initiating page crashed.', |
| /** |
| * Description text for PrerenderFinalStatus::kPrimaryMainFrameRendererProcessKilled. |
| */ |
| prerenderFinalStatusPrimaryMainFrameRendererProcessKilled: 'The initiating page was killed.', |
| /** |
| * Description text for PrerenderFinalStatus::kActivationFramePolicyNotCompatible. |
| */ |
| prerenderFinalStatusActivationFramePolicyNotCompatible: |
| 'The prerender was not used because the sandboxing flags or permissions policy of the initiating page was not compatible with those of the prerendering page.', |
| /** |
| * Description text for PrerenderFinalStatus::kPreloadingDisabled. |
| */ |
| prerenderFinalStatusPreloadingDisabled: |
| 'The prerender was not performed because the user disabled preloading in their browser settings.', |
| /** |
| * Description text for PrerenderFinalStatus::kBatterySaverEnabled. |
| */ |
| prerenderFinalStatusBatterySaverEnabled: |
| 'The prerender was not performed because the user requested that the browser use less battery.', |
| /** |
| * Description text for PrerenderFinalStatus::kActivatedDuringMainFrameNavigation. |
| */ |
| prerenderFinalStatusActivatedDuringMainFrameNavigation: |
| 'Prerendered page activated during initiating page\'s main frame navigation.', |
| /** |
| * Description text for PrerenderFinalStatus::kCrossSiteRedirectInMainFrameNavigation. |
| */ |
| prerenderFinalStatusCrossSiteRedirectInMainFrameNavigation: |
| 'The prerendered page navigated to a URL which redirected to a cross-site URL.', |
| /** |
| * Description text for PrerenderFinalStatus::kCrossSiteNavigationInMainFrameNavigation. |
| */ |
| prerenderFinalStatusCrossSiteNavigationInMainFrameNavigation: 'The prerendered page navigated to a cross-site URL.', |
| /** |
| * Description text for PrerenderFinalStatus::kSameSiteCrossOriginRedirectNotOptInInMainFrameNavigation. |
| */ |
| prerenderFinalStatusSameSiteCrossOriginRedirectNotOptInInMainFrameNavigation: |
| 'The prerendered page navigated to a URL which redirected to a cross-origin same-site URL, but the destination response did not include the appropriate Supports-Loading-Mode header.', |
| /** |
| * Description text for PrerenderFinalStatus::kSameSiteCrossOriginNavigationNotOptInInMainFrameNavigation. |
| */ |
| prerenderFinalStatusSameSiteCrossOriginNavigationNotOptInInMainFrameNavigation: |
| 'The prerendered page navigated to a cross-origin same-site URL, but the destination response did not include the appropriate Supports-Loading-Mode header.', |
| /** |
| * Description text for PrerenderFinalStatus::kMemoryPressureOnTrigger. |
| */ |
| prerenderFinalStatusMemoryPressureOnTrigger: |
| 'The prerender was not performed because the browser was under critical memory pressure.', |
| /** |
| * Description text for PrerenderFinalStatus::kMemoryPressureAfterTriggered. |
| */ |
| prerenderFinalStatusMemoryPressureAfterTriggered: |
| 'The prerendered page was unloaded because the browser came under critical memory pressure.', |
| /** |
| * Description text for PrerenderFinalStatus::kPrerenderingDisabledByDevTools. |
| */ |
| prerenderFinalStatusPrerenderingDisabledByDevTools: |
| 'The prerender was not performed because DevTools has been used to disable prerendering.', |
| }; |
| const str_ = |
| i18n.i18n.registerUIStrings('panels/application/preloading/components/PreloadingDetailsReportView.ts', UIStrings); |
| const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); |
| |
| class PreloadingUIUtils { |
| static action({key}: SDK.PreloadingModel.PreloadingAttempt): string { |
| // Use "prefetch"/"prerender" as is in SpeculationRules. |
| switch (key.action) { |
| case Protocol.Preload.SpeculationAction.Prefetch: |
| return i18n.i18n.lockedString('prefetch'); |
| case Protocol.Preload.SpeculationAction.Prerender: |
| return i18n.i18n.lockedString('prerender'); |
| } |
| } |
| |
| static detailedStatus({status}: SDK.PreloadingModel.PreloadingAttempt): string { |
| // See content/public/browser/preloading.h PreloadingAttemptOutcome. |
| switch (status) { |
| case SDK.PreloadingModel.PreloadingStatus.NotTriggered: |
| return i18nString(UIStrings.detailedStatusNotTriggered); |
| case SDK.PreloadingModel.PreloadingStatus.Pending: |
| return i18nString(UIStrings.detailedStatusPending); |
| case SDK.PreloadingModel.PreloadingStatus.Running: |
| return i18nString(UIStrings.detailedStatusRunning); |
| case SDK.PreloadingModel.PreloadingStatus.Ready: |
| return i18nString(UIStrings.detailedStatusReady); |
| case SDK.PreloadingModel.PreloadingStatus.Success: |
| return i18nString(UIStrings.detailedStatusSuccess); |
| case SDK.PreloadingModel.PreloadingStatus.Failure: |
| return i18nString(UIStrings.detailedStatusFailure); |
| // NotSupported is used to handle unreachable case. For example, |
| // there is no code path for |
| // PreloadingTriggeringOutcome::kTriggeredButPending in prefetch, |
| // which is mapped to NotSupported. So, we regard it as an |
| // internal error. |
| case SDK.PreloadingModel.PreloadingStatus.NotSupported: |
| return i18n.i18n.lockedString('Internal error'); |
| } |
| } |
| |
| // Detailed failure reason for PrerenderFinalStatus. |
| static failureReason({prerenderStatus}: SDK.PreloadingModel.PrerenderAttempt): string|null { |
| // If you face an error on rolling CDP changes, see |
| // https://docs.google.com/document/d/1PnrfowsZMt62PX1EvvTp2Nqs3ji1zrklrAEe1JYbkTk |
| switch (prerenderStatus) { |
| case null: |
| case Protocol.Preload.PrerenderFinalStatus.Activated: |
| return null; |
| case Protocol.Preload.PrerenderFinalStatus.Destroyed: |
| // TODO(https://crbug.com/1410709): Fill it. |
| return i18n.i18n.lockedString('Unknown'); |
| case Protocol.Preload.PrerenderFinalStatus.LowEndDevice: |
| return i18nString(UIStrings.prerenderFinalStatusLowEndDevice); |
| case Protocol.Preload.PrerenderFinalStatus.InvalidSchemeRedirect: |
| return i18nString(UIStrings.prerenderFinalStatusInvalidSchemeRedirect); |
| case Protocol.Preload.PrerenderFinalStatus.InvalidSchemeNavigation: |
| return i18nString(UIStrings.prerenderFinalStatusInvalidSchemeNavigation); |
| case Protocol.Preload.PrerenderFinalStatus.InProgressNavigation: |
| // Not used. |
| return i18n.i18n.lockedString('Internal error'); |
| case Protocol.Preload.PrerenderFinalStatus.NavigationRequestBlockedByCsp: |
| return i18nString(UIStrings.prerenderFinalStatusNavigationRequestBlockedByCsp); |
| case Protocol.Preload.PrerenderFinalStatus.MainFrameNavigation: |
| return i18nString(UIStrings.prerenderFinalStatusMainFrameNavigation); |
| case Protocol.Preload.PrerenderFinalStatus.MojoBinderPolicy: |
| // TODO(https://crbug.com/1410709): Improve these messages with disallowedApiMethod. |
| return i18nString(UIStrings.prerenderFinalStatusMojoBinderPolicy); |
| case Protocol.Preload.PrerenderFinalStatus.RendererProcessCrashed: |
| return i18nString(UIStrings.prerenderFinalStatusRendererProcessCrashed); |
| case Protocol.Preload.PrerenderFinalStatus.RendererProcessKilled: |
| return i18nString(UIStrings.prerenderFinalStatusRendererProcessKilled); |
| case Protocol.Preload.PrerenderFinalStatus.Download: |
| return i18nString(UIStrings.prerenderFinalStatusDownload); |
| case Protocol.Preload.PrerenderFinalStatus.TriggerDestroyed: |
| // After https://crrev.com/c/4515841, this won't occur if DevTools is opened. |
| return i18n.i18n.lockedString('Internal error'); |
| case Protocol.Preload.PrerenderFinalStatus.NavigationNotCommitted: |
| // This looks internal error. |
| // |
| // TODO(https://crbug.com/1410709): Fill it. |
| return i18n.i18n.lockedString('Internal error'); |
| case Protocol.Preload.PrerenderFinalStatus.NavigationBadHttpStatus: |
| return i18nString(UIStrings.prerenderFinalStatusNavigationBadHttpStatus); |
| case Protocol.Preload.PrerenderFinalStatus.ClientCertRequested: |
| return i18nString(UIStrings.prerenderFinalStatusClientCertRequested); |
| case Protocol.Preload.PrerenderFinalStatus.NavigationRequestNetworkError: |
| return i18nString(UIStrings.prerenderFinalStatusNavigationRequestNetworkError); |
| case Protocol.Preload.PrerenderFinalStatus.MaxNumOfRunningPrerendersExceeded: |
| return i18nString(UIStrings.prerenderFinalStatusMaxNumOfRunningPrerendersExceeded); |
| case Protocol.Preload.PrerenderFinalStatus.CancelAllHostsForTesting: |
| // Used only in tests. |
| throw new Error('unreachable'); |
| case Protocol.Preload.PrerenderFinalStatus.DidFailLoad: |
| // TODO(https://crbug.com/1410709): Fill it. |
| return i18n.i18n.lockedString('Unknown'); |
| case Protocol.Preload.PrerenderFinalStatus.Stop: |
| // TODO(https://crbug.com/1410709): Fill it. |
| return i18n.i18n.lockedString('Unknown'); |
| case Protocol.Preload.PrerenderFinalStatus.SslCertificateError: |
| return i18nString(UIStrings.prerenderFinalStatusSslCertificateError); |
| case Protocol.Preload.PrerenderFinalStatus.LoginAuthRequested: |
| return i18nString(UIStrings.prerenderFinalStatusLoginAuthRequested); |
| case Protocol.Preload.PrerenderFinalStatus.UaChangeRequiresReload: |
| return i18nString(UIStrings.prerenderFinalStatusUaChangeRequiresReload); |
| case Protocol.Preload.PrerenderFinalStatus.BlockedByClient: |
| return i18nString(UIStrings.prerenderFinalStatusBlockedByClient); |
| case Protocol.Preload.PrerenderFinalStatus.AudioOutputDeviceRequested: |
| return i18nString(UIStrings.prerenderFinalStatusAudioOutputDeviceRequested); |
| case Protocol.Preload.PrerenderFinalStatus.MixedContent: |
| return i18nString(UIStrings.prerenderFinalStatusMixedContent); |
| case Protocol.Preload.PrerenderFinalStatus.TriggerBackgrounded: |
| return i18nString(UIStrings.prerenderFinalStatusTriggerBackgrounded); |
| case Protocol.Preload.PrerenderFinalStatus.EmbedderTriggeredAndCrossOriginRedirected: |
| // Not used. |
| return i18n.i18n.lockedString('Internal error'); |
| case Protocol.Preload.PrerenderFinalStatus.MemoryLimitExceeded: |
| return i18nString(UIStrings.prerenderFinalStatusMemoryLimitExceeded); |
| case Protocol.Preload.PrerenderFinalStatus.FailToGetMemoryUsage: |
| return i18nString(UIStrings.prerenderFinalStatusFailToGetMemoryUsage); |
| case Protocol.Preload.PrerenderFinalStatus.DataSaverEnabled: |
| return i18nString(UIStrings.prerenderFinalStatusDataSaverEnabled); |
| case Protocol.Preload.PrerenderFinalStatus.HasEffectiveUrl: |
| return i18nString(UIStrings.prerenderFinalStatusHasEffectiveUrl); |
| case Protocol.Preload.PrerenderFinalStatus.ActivatedBeforeStarted: |
| // Status for debugging. |
| return i18n.i18n.lockedString('Internal error'); |
| case Protocol.Preload.PrerenderFinalStatus.InactivePageRestriction: |
| // This looks internal error. |
| // |
| // TODO(https://crbug.com/1410709): Fill it. |
| return i18n.i18n.lockedString('Internal error'); |
| case Protocol.Preload.PrerenderFinalStatus.StartFailed: |
| // This looks internal error. |
| // |
| // TODO(https://crbug.com/1410709): Fill it. |
| return i18n.i18n.lockedString('Internal error'); |
| case Protocol.Preload.PrerenderFinalStatus.TimeoutBackgrounded: |
| return i18nString(UIStrings.prerenderFinalStatusTimeoutBackgrounded); |
| case Protocol.Preload.PrerenderFinalStatus.CrossSiteRedirectInInitialNavigation: |
| return i18nString(UIStrings.prerenderFinalStatusCrossSiteRedirectInInitialNavigation); |
| case Protocol.Preload.PrerenderFinalStatus.CrossSiteNavigationInInitialNavigation: |
| return i18nString(UIStrings.prerenderFinalStatusCrossSiteNavigationInInitialNavigation); |
| case Protocol.Preload.PrerenderFinalStatus.SameSiteCrossOriginRedirectNotOptInInInitialNavigation: |
| return i18nString(UIStrings.prerenderFinalStatusSameSiteCrossOriginRedirectNotOptInInInitialNavigation); |
| case Protocol.Preload.PrerenderFinalStatus.SameSiteCrossOriginNavigationNotOptInInInitialNavigation: |
| return i18nString(UIStrings.prerenderFinalStatusSameSiteCrossOriginNavigationNotOptInInInitialNavigation); |
| case Protocol.Preload.PrerenderFinalStatus.ActivationNavigationParameterMismatch: |
| return i18nString(UIStrings.prerenderFinalStatusActivationNavigationParameterMismatch); |
| case Protocol.Preload.PrerenderFinalStatus.ActivatedInBackground: |
| // Status for debugging. |
| return i18n.i18n.lockedString('Internal error'); |
| case Protocol.Preload.PrerenderFinalStatus.EmbedderHostDisallowed: |
| // Chrome as embedder doesn't use this. |
| throw new Error('unreachable'); |
| case Protocol.Preload.PrerenderFinalStatus.ActivationNavigationDestroyedBeforeSuccess: |
| // Should not occur in DevTools because tab is alive? |
| return i18n.i18n.lockedString('Internal error'); |
| case Protocol.Preload.PrerenderFinalStatus.TabClosedByUserGesture: |
| // Should not occur in DevTools because tab is alive. |
| throw new Error('unreachable'); |
| case Protocol.Preload.PrerenderFinalStatus.TabClosedWithoutUserGesture: |
| // Should not occur in DevTools because tab is alive. |
| throw new Error('unreachable'); |
| case Protocol.Preload.PrerenderFinalStatus.PrimaryMainFrameRendererProcessCrashed: |
| return i18nString(UIStrings.prerenderFinalStatusPrimaryMainFrameRendererProcessCrashed); |
| case Protocol.Preload.PrerenderFinalStatus.PrimaryMainFrameRendererProcessKilled: |
| return i18nString(UIStrings.prerenderFinalStatusPrimaryMainFrameRendererProcessKilled); |
| case Protocol.Preload.PrerenderFinalStatus.ActivationFramePolicyNotCompatible: |
| return i18nString(UIStrings.prerenderFinalStatusActivationFramePolicyNotCompatible); |
| case Protocol.Preload.PrerenderFinalStatus.PreloadingDisabled: |
| return i18nString(UIStrings.prerenderFinalStatusPreloadingDisabled); |
| case Protocol.Preload.PrerenderFinalStatus.BatterySaverEnabled: |
| return i18nString(UIStrings.prerenderFinalStatusBatterySaverEnabled); |
| case Protocol.Preload.PrerenderFinalStatus.ActivatedDuringMainFrameNavigation: |
| return i18nString(UIStrings.prerenderFinalStatusActivatedDuringMainFrameNavigation); |
| case Protocol.Preload.PrerenderFinalStatus.PreloadingUnsupportedByWebContents: |
| // Chrome as embedder doesn't use this. |
| throw new Error('unreachable'); |
| case Protocol.Preload.PrerenderFinalStatus.CrossSiteRedirectInMainFrameNavigation: |
| return i18nString(UIStrings.prerenderFinalStatusCrossSiteRedirectInMainFrameNavigation); |
| case Protocol.Preload.PrerenderFinalStatus.CrossSiteNavigationInMainFrameNavigation: |
| return i18nString(UIStrings.prerenderFinalStatusCrossSiteNavigationInMainFrameNavigation); |
| case Protocol.Preload.PrerenderFinalStatus.SameSiteCrossOriginRedirectNotOptInInMainFrameNavigation: |
| return i18nString(UIStrings.prerenderFinalStatusSameSiteCrossOriginRedirectNotOptInInMainFrameNavigation); |
| case Protocol.Preload.PrerenderFinalStatus.SameSiteCrossOriginNavigationNotOptInInMainFrameNavigation: |
| return i18nString(UIStrings.prerenderFinalStatusSameSiteCrossOriginNavigationNotOptInInMainFrameNavigation); |
| case Protocol.Preload.PrerenderFinalStatus.MemoryPressureOnTrigger: |
| return i18nString(UIStrings.prerenderFinalStatusMemoryPressureOnTrigger); |
| case Protocol.Preload.PrerenderFinalStatus.MemoryPressureAfterTriggered: |
| return i18nString(UIStrings.prerenderFinalStatusMemoryPressureAfterTriggered); |
| case Protocol.Preload.PrerenderFinalStatus.PrerenderingDisabledByDevTools: |
| return i18nString(UIStrings.prerenderFinalStatusPrerenderingDisabledByDevTools); |
| default: |
| // Note that we use switch and exhaustiveness check to prevent to |
| // forget updating these strings, but allow to handle unknown |
| // PrerenderFinalStatus at runtime. |
| return i18n.i18n.lockedString(`Unknown failure reason: ${ |
| prerenderStatus as 'See https://docs.google.com/document/d/1PnrfowsZMt62PX1EvvTp2Nqs3ji1zrklrAEe1JYbkTk'}`); |
| } |
| } |
| |
| // Decoding PrefetchFinalStatus prefetchAttempt to failure description. |
| static prefetchAttemptToFailureDescription({prefetchStatus}: SDK.PreloadingModel.PrefetchAttempt): string|null { |
| // If you face an error on rolling CDP changes, see |
| // https://docs.google.com/document/d/1PnrfowsZMt62PX1EvvTp2Nqs3ji1zrklrAEe1JYbkTk |
| switch (prefetchStatus) { |
| case null: |
| return null; |
| // PrefetchNotStarted is mapped to Pending. |
| case Protocol.Preload.PrefetchStatus.PrefetchNotStarted: |
| return null; |
| // PrefetchNotFinishedInTime is mapped to Running. |
| case Protocol.Preload.PrefetchStatus.PrefetchNotFinishedInTime: |
| return null; |
| // PrefetchResponseUsed is mapped to Success. |
| case Protocol.Preload.PrefetchStatus.PrefetchResponseUsed: |
| return null; |
| // Holdback related status is expected to be overridden when DevTools is |
| // opened. |
| case Protocol.Preload.PrefetchStatus.PrefetchAllowed: |
| case Protocol.Preload.PrefetchStatus.PrefetchHeldback: |
| return null; |
| // TODO(https://crbug.com/1410709): deprecate PrefetchSuccessfulButNotUsed in the protocol. |
| case Protocol.Preload.PrefetchStatus.PrefetchSuccessfulButNotUsed: |
| return null; |
| case Protocol.Preload.PrefetchStatus.PrefetchFailedIneligibleRedirect: |
| return PrefetchReasonDescription['PrefetchFailedIneligibleRedirect'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchFailedInvalidRedirect: |
| return PrefetchReasonDescription['PrefetchFailedInvalidRedirect'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchFailedMIMENotSupported: |
| return PrefetchReasonDescription['PrefetchFailedMIMENotSupported'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchFailedNetError: |
| return PrefetchReasonDescription['PrefetchFailedNetError'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchFailedNon2XX: |
| return PrefetchReasonDescription['PrefetchFailedNon2XX'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchFailedPerPageLimitExceeded: |
| return PrefetchReasonDescription['PrefetchFailedPerPageLimitExceeded'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchIneligibleRetryAfter: |
| return PrefetchReasonDescription['PrefetchIneligibleRetryAfter'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchEvicted: |
| return PrefetchReasonDescription['PrefetchEvicted'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchIsPrivacyDecoy: |
| return PrefetchReasonDescription['PrefetchIsPrivacyDecoy'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchIsStale: |
| return PrefetchReasonDescription['PrefetchIsStale'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchNotEligibleBrowserContextOffTheRecord: |
| return PrefetchReasonDescription['PrefetchNotEligibleBrowserContextOffTheRecord'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchNotEligibleDataSaverEnabled: |
| return PrefetchReasonDescription['PrefetchNotEligibleDataSaverEnabled'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchNotEligibleExistingProxy: |
| return PrefetchReasonDescription['PrefetchNotEligibleExistingProxy'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchNotEligibleHostIsNonUnique: |
| return PrefetchReasonDescription['PrefetchNotEligibleHostIsNonUnique'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchNotEligibleNonDefaultStoragePartition: |
| return PrefetchReasonDescription['PrefetchNotEligibleNonDefaultStoragePartition'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchNotEligibleSameSiteCrossOriginPrefetchRequiredProxy: |
| return PrefetchReasonDescription['PrefetchNotEligibleSameSiteCrossOriginPrefetchRequiredProxy'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchNotEligibleSchemeIsNotHttps: |
| return PrefetchReasonDescription['PrefetchNotEligibleSchemeIsNotHttps'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchNotEligibleUserHasCookies: |
| return PrefetchReasonDescription['PrefetchNotEligibleUserHasCookies'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchNotEligibleUserHasServiceWorker: |
| return PrefetchReasonDescription['PrefetchNotEligibleUserHasServiceWorker'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchNotUsedCookiesChanged: |
| return PrefetchReasonDescription['PrefetchNotUsedCookiesChanged'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchProxyNotAvailable: |
| return PrefetchReasonDescription['PrefetchProxyNotAvailable'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchNotUsedProbeFailed: |
| return PrefetchReasonDescription['PrefetchNotUsedProbeFailed'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchNotEligibleBatterySaverEnabled: |
| return PrefetchReasonDescription['PrefetchNotEligibleBatterySaverEnabled'].name(); |
| case Protocol.Preload.PrefetchStatus.PrefetchNotEligiblePreloadingDisabled: |
| return PrefetchReasonDescription['PrefetchNotEligiblePreloadingDisabled'].name(); |
| default: |
| // Note that we use switch and exhaustiveness check to prevent to |
| // forget updating these strings, but allow to handle unknown |
| // PrefetchStatus at runtime. |
| return i18n.i18n.lockedString(`Unknown failure reason: ${ |
| prefetchStatus as 'See https://docs.google.com/document/d/1PnrfowsZMt62PX1EvvTp2Nqs3ji1zrklrAEe1JYbkTk'}`); |
| } |
| } |
| } |
| |
| const coordinator = Coordinator.RenderCoordinator.RenderCoordinator.instance(); |
| |
| export type PreloadingDetailsReportViewData = PreloadingDetailsReportViewDataInternal|null; |
| interface PreloadingDetailsReportViewDataInternal { |
| preloadingAttempt: SDK.PreloadingModel.PreloadingAttempt; |
| ruleSets: Protocol.Preload.RuleSet[]; |
| } |
| |
| export class PreloadingDetailsReportView extends HTMLElement { |
| static readonly litTagName = LitHtml.literal`devtools-resources-preloading-details-report-view`; |
| |
| readonly #shadow = this.attachShadow({mode: 'open'}); |
| #data: PreloadingDetailsReportViewData = null; |
| |
| connectedCallback(): void { |
| this.#shadow.adoptedStyleSheets = [preloadingDetailsReportViewStyles]; |
| } |
| |
| set data(data: PreloadingDetailsReportViewData) { |
| this.#data = data; |
| void this.#render(); |
| } |
| |
| async #render(): Promise<void> { |
| await coordinator.write('PreloadingDetailsReportView render', () => { |
| if (this.#data === null) { |
| // Disabled until https://crbug.com/1079231 is fixed. |
| // clang-format off |
| LitHtml.render(LitHtml.html` |
| <div class="preloading-noselected"> |
| <div> |
| <p>${i18nString(UIStrings.selectAnElementForMoreDetails)}</p> |
| </div> |
| </div> |
| `, this.#shadow, {host: this}); |
| // clang-format on |
| return; |
| } |
| |
| const action = PreloadingUIUtils.action(this.#data.preloadingAttempt); |
| const detailedStatus = PreloadingUIUtils.detailedStatus(this.#data.preloadingAttempt); |
| |
| // Disabled until https://crbug.com/1079231 is fixed. |
| // clang-format off |
| LitHtml.render(LitHtml.html` |
| <${ReportView.ReportView.Report.litTagName} .data=${{reportTitle: 'Preloading Attempt'} as ReportView.ReportView.ReportData}> |
| <${ReportView.ReportView.ReportSectionHeader.litTagName}>${i18nString(UIStrings.detailsDetailedInformation)}</${ |
| ReportView.ReportView.ReportSectionHeader.litTagName}> |
| |
| <${ReportView.ReportView.ReportKey.litTagName}>${i18n.i18n.lockedString('URL')}</${ |
| ReportView.ReportView.ReportKey.litTagName}> |
| <${ReportView.ReportView.ReportValue.litTagName}> |
| <div class="text-ellipsis" title=${this.#data.preloadingAttempt.key.url}>${this.#data.preloadingAttempt.key.url}</div> |
| </${ReportView.ReportView.ReportValue.litTagName}> |
| |
| <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.detailsAction)}</${ |
| ReportView.ReportView.ReportKey.litTagName}> |
| <${ReportView.ReportView.ReportValue.litTagName}> |
| <div class="text-ellipsis" title=""> |
| ${action} |
| </div> |
| </${ReportView.ReportView.ReportValue.litTagName}> |
| |
| <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.detailsStatus)}</${ |
| ReportView.ReportView.ReportKey.litTagName}> |
| <${ReportView.ReportView.ReportValue.litTagName}> |
| ${detailedStatus} |
| </${ReportView.ReportView.ReportValue.litTagName}> |
| |
| ${this.#maybePrefetchFailureReason()} |
| ${this.#maybePrerenderFailureReason()} |
| |
| ${this.#data.ruleSets.map(ruleSet => this.#renderRuleSet(ruleSet))} |
| </${ReportView.ReportView.Report.litTagName}> |
| `, this.#shadow, {host: this}); |
| // clang-format on |
| }); |
| } |
| |
| #maybePrefetchFailureReason(): LitHtml.LitTemplate { |
| assertNotNullOrUndefined(this.#data); |
| const attempt = this.#data.preloadingAttempt; |
| |
| if (attempt.action !== Protocol.Preload.SpeculationAction.Prefetch) { |
| return LitHtml.nothing; |
| } |
| |
| const failureDescription = PreloadingUIUtils.prefetchAttemptToFailureDescription(attempt); |
| if (failureDescription === null) { |
| return LitHtml.nothing; |
| } |
| |
| return LitHtml.html` |
| <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.detailsFailureReason)}</${ |
| ReportView.ReportView.ReportKey.litTagName}> |
| <${ReportView.ReportView.ReportValue.litTagName}> |
| ${failureDescription} |
| </${ReportView.ReportView.ReportValue.litTagName}> |
| `; |
| } |
| |
| #maybePrerenderFailureReason(): LitHtml.LitTemplate { |
| assertNotNullOrUndefined(this.#data); |
| const attempt = this.#data.preloadingAttempt; |
| |
| if (attempt.action !== Protocol.Preload.SpeculationAction.Prerender) { |
| return LitHtml.nothing; |
| } |
| |
| const failureReason = PreloadingUIUtils.failureReason(attempt); |
| if (failureReason === null) { |
| return LitHtml.nothing; |
| } |
| |
| return LitHtml.html` |
| <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.detailsFailureReason)}</${ |
| ReportView.ReportView.ReportKey.litTagName}> |
| <${ReportView.ReportView.ReportValue.litTagName}> |
| ${failureReason} |
| </${ReportView.ReportView.ReportValue.litTagName}> |
| `; |
| } |
| |
| #renderRuleSet(ruleSet: Protocol.Preload.RuleSet): LitHtml.LitTemplate { |
| // We can assume `sourceText` is a valid JSON because this triggered the preloading attempt. |
| const json = JSON.stringify(JSON.parse(ruleSet.sourceText)); |
| |
| // Disabled until https://crbug.com/1079231 is fixed. |
| // clang-format off |
| return LitHtml.html` |
| <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.detailsRuleSet)}</${ |
| ReportView.ReportView.ReportKey.litTagName}> |
| <${ReportView.ReportView.ReportValue.litTagName}> |
| <div class="text-ellipsis" title=""> |
| ${json} |
| </div> |
| </${ReportView.ReportView.ReportValue.litTagName}> |
| `; |
| // clang-format on |
| } |
| } |
| |
| ComponentHelpers.CustomElements.defineComponent( |
| 'devtools-resources-preloading-details-report-view', PreloadingDetailsReportView); |
| |
| declare global { |
| // eslint-disable-next-line @typescript-eslint/no-unused-vars |
| interface HTMLElementTagNameMap { |
| 'devtools-resources-preloading-details-report-view': PreloadingDetailsReportView; |
| } |
| } |