BrowserGlue.jsm 138 KB
Newer Older
1
2
3
/* 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/. */
4

5
6
var EXPORTED_SYMBOLS = ["BrowserGlue", "ContentPermissionPrompt"];

7
8
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

9
10
11
12
13
14
15
const { XPCOMUtils } = ChromeUtils.import(
  "resource://gre/modules/XPCOMUtils.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { AppConstants } = ChromeUtils.import(
  "resource://gre/modules/AppConstants.jsm"
);
16

17
18
19
20
21
ChromeUtils.defineModuleGetter(
  this,
  "ActorManagerParent",
  "resource://gre/modules/ActorManagerParent.jsm"
);
22

23
24
const PREF_PDFJS_ENABLED_CACHE_STATE = "pdfjs.enabledCache.state";

25
let ACTORS = {
26
27
28
29
30
31
32
33
  BrowserTab: {
    parent: {
      moduleURI: "resource:///actors/BrowserTabParent.jsm",
    },
    child: {
      moduleURI: "resource:///actors/BrowserTabChild.jsm",

      events: {
34
35
        DOMWindowCreated: {},
        MozAfterPaint: {},
36
37
38
39
40
41
42
43
44
45
46
47
48
        "MozDOMPointerLock:Entered": {},
        "MozDOMPointerLock:Exited": {},
      },
      messages: [
        "Browser:Reload",
        "Browser:AppTab",
        "Browser:HasSiblings",
        "MixedContent:ReenableProtection",
        "UpdateCharacterSet",
      ],
    },
  },

49
50
51
52
53
54
55
56
  ContextMenu: {
    parent: {
      moduleURI: "resource:///actors/ContextMenuParent.jsm",
    },

    child: {
      moduleURI: "resource:///actors/ContextMenuChild.jsm",
      events: {
57
        contextmenu: { mozSystemGroup: true },
58
59
60
61
62
63
      },
    },

    allFrames: true,
  },

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  FormValidation: {
    parent: {
      moduleURI: "resource:///actors/FormValidationParent.jsm",
    },

    child: {
      moduleURI: "resource:///actors/FormValidationChild.jsm",
      events: {
        MozInvalidForm: {},
      },
      messages: ["FormValidation:ShowPopup", "FormValidation:HidePopup"],
    },

    allFrames: true,
  },

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  Plugin: {
    parent: {
      moduleURI: "resource:///actors/PluginParent.jsm",
      messages: [
        "PluginContent:ShowClickToPlayNotification",
        "PluginContent:RemoveNotification",
        "PluginContent:ShowPluginCrashedNotification",
        "PluginContent:SubmitReport",
        "PluginContent:LinkClickCallback",
        "PluginContent:GetCrashData",
      ],
    },
    child: {
      moduleURI: "resource:///actors/PluginChild.jsm",
      events: {
        PluginBindingAttached: { capture: true, wantUntrusted: true },
        PluginCrashed: { capture: true },
        PluginOutdated: { capture: true },
        PluginInstantiated: { capture: true },
        PluginRemoved: { capture: true },
        HiddenPlugin: { capture: true },
      },

      messages: [
        "PluginParent:ActivatePlugins",
        "PluginParent:Test:ClearCrashData",
      ],

      observers: ["decoder-doctor-notification"],
    },

    allFrames: true,
  },

114
115
116
117
118
119
120
121
  Prompt: {
    parent: {
      moduleURI: "resource:///actors/PromptParent.jsm",
    },

    allFrames: true,
  },

122
123
124
125
  SwitchDocumentDirection: {
    child: {
      moduleURI: "resource:///actors/SwitchDocumentDirectionChild.jsm",

126
      messages: ["SwitchDocumentDirection"],
127
128
129
130
131
    },

    allFrames: true,
  },

132
133
134
135
136
137
138
139
140
141
142
  SubframeCrash: {
    parent: {
      moduleURI: "resource:///actors/SubframeCrashParent.jsm",
    },

    child: {
      moduleURI: "resource:///actors/SubframeCrashChild.jsm",
    },

    allFrames: true,
  },
143
144
145
};

let LEGACY_ACTORS = {
146
147
  AboutLogins: {
    child: {
148
      matches: ["about:logins", "about:logins?*"],
149
150
      module: "resource:///actors/AboutLoginsChild.jsm",
      events: {
151
        AboutLoginsCopyLoginDetail: { wantUntrusted: true },
152
153
        AboutLoginsCreateLogin: { wantUntrusted: true },
        AboutLoginsDeleteLogin: { wantUntrusted: true },
154
        AboutLoginsDismissBreachAlert: { wantUntrusted: true },
155
        AboutLoginsHideFooter: { wantUntrusted: true },
156
157
158
159
        AboutLoginsImport: { wantUntrusted: true },
        AboutLoginsInit: { wantUntrusted: true },
        AboutLoginsOpenFAQ: { wantUntrusted: true },
        AboutLoginsOpenFeedback: { wantUntrusted: true },
160
161
        AboutLoginsOpenMobileAndroid: { wantUntrusted: true },
        AboutLoginsOpenMobileIos: { wantUntrusted: true },
162
163
164
        AboutLoginsOpenPreferences: { wantUntrusted: true },
        AboutLoginsOpenSite: { wantUntrusted: true },
        AboutLoginsRecordTelemetryEvent: { wantUntrusted: true },
165
166
        AboutLoginsSyncEnable: { wantUntrusted: true },
        AboutLoginsSyncOptions: { wantUntrusted: true },
167
        AboutLoginsUpdateLogin: { wantUntrusted: true },
168
169
170
      },
      messages: [
        "AboutLogins:AllLogins",
171
        "AboutLogins:LocalizeBadges",
172
173
174
        "AboutLogins:LoginAdded",
        "AboutLogins:LoginModified",
        "AboutLogins:LoginRemoved",
175
        "AboutLogins:MasterPasswordResponse",
176
        "AboutLogins:SendFavicons",
177
178
        "AboutLogins:SyncState",
        "AboutLogins:UpdateBreaches",
179
180
181
182
      ],
    },
  },

183
184
185
186
187
  AboutReader: {
    child: {
      module: "resource:///actors/AboutReaderChild.jsm",
      group: "browsers",
      events: {
188
189
190
191
        AboutReaderContentLoaded: { wantUntrusted: true },
        DOMContentLoaded: {},
        pageshow: { mozSystemGroup: true },
        pagehide: { mozSystemGroup: true },
192
      },
193
      messages: ["Reader:ToggleReaderMode", "Reader:PushState"],
194
195
196
    },
  },

197
198
199
200
  BlockedSite: {
    child: {
      module: "resource:///actors/BlockedSiteChild.jsm",
      events: {
201
202
        AboutBlockedLoaded: { wantUntrusted: true },
        click: {},
203
204
205
      },
      matches: ["about:blocked?*"],
      allFrames: true,
206
      messages: ["DeceptiveBlockedDetails"],
207
208
209
    },
  },

210
211
212
213
  ClickHandler: {
    child: {
      module: "resource:///actors/ClickHandlerChild.jsm",
      events: {
214
215
        click: { capture: true, mozSystemGroup: true },
        auxclick: { capture: true, mozSystemGroup: true },
216
      },
217
218
219
    },
  },

220
221
222
223
  ContentSearch: {
    child: {
      module: "resource:///actors/ContentSearchChild.jsm",
      group: "browsers",
224
225
226
227
228
229
230
      matches: [
        "about:home",
        "about:newtab",
        "about:welcome",
        "about:privatebrowsing",
        "chrome://mochitests/content/*",
      ],
231
      events: {
232
        ContentSearchClient: { capture: true, wantUntrusted: true },
233
      },
234
      messages: ["ContentSearch"],
235
236
237
    },
  },

238
239
240
241
  ContextMenuSpecialProcess: {
    child: {
      module: "resource:///actors/ContextMenuSpecialProcessChild.jsm",
      events: {
242
        contextmenu: { mozSystemGroup: true },
243
244
245
246
247
      },
    },
    allFrames: true,
  },

248
249
250
251
252
253
254
255
256
257
258
  DOMFullscreen: {
    child: {
      module: "resource:///actors/DOMFullscreenChild.jsm",
      group: "browsers",
      events: {
        "MozDOMFullscreen:Request": {},
        "MozDOMFullscreen:Entered": {},
        "MozDOMFullscreen:NewOrigin": {},
        "MozDOMFullscreen:Exit": {},
        "MozDOMFullscreen:Exited": {},
      },
259
      messages: ["DOMFullscreen:Entered", "DOMFullscreen:CleanUp"],
260
261
262
    },
  },

263
264
265
266
267
  LightweightTheme: {
    child: {
      module: "resource:///actors/LightweightThemeChild.jsm",
      matches: ["about:home", "about:newtab", "about:welcome"],
      events: {
268
        pageshow: { mozSystemGroup: true },
269
270
271
272
      },
    },
  },

273
274
275
276
  LinkHandler: {
    child: {
      module: "resource:///actors/LinkHandlerChild.jsm",
      events: {
277
278
279
280
281
        DOMHeadElementParsed: {},
        DOMLinkAdded: {},
        DOMLinkChanged: {},
        pageshow: {},
        pagehide: {},
282
283
284
285
      },
    },
  },

286
287
288
289
  NetError: {
    child: {
      module: "resource:///actors/NetErrorChild.jsm",
      events: {
290
291
292
293
        AboutNetErrorLoad: { wantUntrusted: true },
        AboutNetErrorSetAutomatic: { wantUntrusted: true },
        AboutNetErrorResetPreferences: { wantUntrusted: true },
        click: {},
294
295
296
297
298
299
      },
      matches: ["about:certerror?*", "about:neterror?*"],
      allFrames: true,
    },
  },

300
301
302
303
  OfflineApps: {
    child: {
      module: "resource:///actors/OfflineAppsChild.jsm",
      events: {
304
        MozApplicationManifest: {},
305
      },
306
      messages: ["OfflineApps:StartFetching"],
307
308
309
    },
  },

310
311
312
313
314
  PageInfo: {
    child: {
      module: "resource:///actors/PageInfoChild.jsm",
      messages: ["PageInfo:getData"],
    },
315
316
  },

317
318
319
320
321
  PageStyle: {
    child: {
      module: "resource:///actors/PageStyleChild.jsm",
      group: "browsers",
      events: {
322
        pageshow: {},
323
      },
324
      messages: ["PageStyle:Switch", "PageStyle:Disable"],
325
326
327
      // Only matching web pages, as opposed to internal about:, chrome: or
      // resource: pages. See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns
      matches: ["*://*/*"],
328
329
    },
  },
330

331
332
333
334
335
  RFPHelper: {
    child: {
      module: "resource:///actors/RFPHelperChild.jsm",
      group: "browsers",
      events: {
336
        resize: {},
337
      },
338
      messages: ["Finder:FindbarOpen", "Finder:FindbarClose"],
339
340
341
    },
  },

342
343
344
345
346
  SearchTelemetry: {
    child: {
      module: "resource:///actors/SearchTelemetryChild.jsm",
      events: {
        DOMContentLoaded: {},
347
        pageshow: { mozSystemGroup: true },
348
349
350
351
      },
    },
  },

352
353
354
355
  ShieldFrame: {
    child: {
      module: "resource://normandy-content/ShieldFrameChild.jsm",
      events: {
356
        ShieldPageEvent: { wantUntrusted: true },
357
358
359
360
361
      },
      matches: ["about:studies"],
    },
  },

362
363
364
365
  UITour: {
    child: {
      module: "resource:///modules/UITourChild.jsm",
      events: {
366
        mozUITour: { wantUntrusted: true },
367
368
369
370
371
      },
      permissions: ["uitour"],
    },
  },

372
373
374
375
376
377
378
  URIFixup: {
    child: {
      module: "resource:///actors/URIFixupChild.jsm",
      group: "browsers",
      observers: ["keyword-uri-fixup"],
    },
  },
379
380
381
382
383
384
385
386
387
388
389
390
391

  WebRTC: {
    child: {
      module: "resource:///actors/WebRTCChild.jsm",
      messages: [
        "rtcpeer:Allow",
        "rtcpeer:Deny",
        "webrtc:Allow",
        "webrtc:Deny",
        "webrtc:StopSharing",
      ],
    },
  },
392
393
};

394
(function earlyBlankFirstPaint() {
395
396
397
398
  if (
    AppConstants.platform == "macosx" ||
    !Services.prefs.getBoolPref("browser.startup.blankWindow", false)
  ) {
399
    return;
400
  }
401

402
403
  // Until bug 1450626 and bug 1488384 are fixed, skip the blank window when
  // using a non-default theme.
404
405
406
407
408
409
  if (
    Services.prefs.getCharPref(
      "extensions.activeThemeID",
      "default-theme@mozilla.org"
    ) != "default-theme@mozilla.org"
  ) {
410
    return;
411
  }
412

413
  let store = Services.xulStore;
414
  let getValue = attr =>
415
    store.getValue(AppConstants.BROWSER_CHROME_URL, "main-window", attr);
416
417
418
419
  let width = getValue("width");
  let height = getValue("height");

  // The clean profile case isn't handled yet. Return early for now.
420
  if (!width || !height) {
421
    return;
422
  }
423
424
425

  let browserWindowFeatures =
    "chrome,all,dialog=no,extrachrome,menubar,resizable,scrollbars,status," +
426
    "location,toolbar,personalbar";
427
428
429
430
431
432
433
  let win = Services.ww.openWindow(
    null,
    "about:blank",
    null,
    browserWindowFeatures,
    null
  );
434
435

  // Hide the titlebar if the actual browser window will draw in it.
436
437
438
439
  let hiddenTitlebar = Services.prefs.getBoolPref(
    "browser.tabs.drawInTitlebar",
    win.matchMedia("(-moz-gtk-csd-hide-titlebar-by-default)").matches
  );
440
  if (hiddenTitlebar) {
441
    win.windowUtils.setChromeMargin(0, 2, 2, 2);
442
443
  }

444
445
446
  let docElt = win.document.documentElement;
  docElt.setAttribute("screenX", getValue("screenX"));
  docElt.setAttribute("screenY", getValue("screenY"));
447
448
449
450
451
452

  // The sizemode="maximized" attribute needs to be set before first paint.
  let sizemode = getValue("sizemode");
  if (sizemode == "maximized") {
    docElt.setAttribute("sizemode", sizemode);

453
454
455
456
    // Set the size to use when the user leaves the maximized mode.
    // The persisted size is the outer size, but the height/width
    // attributes set the inner size.
    let xulWin = win.docShell.treeOwner
457
458
      .QueryInterface(Ci.nsIInterfaceRequestor)
      .getInterface(Ci.nsIXULWindow);
459
460
    height -= xulWin.outerToInnerHeightDifferenceInCSSPixels;
    width -= xulWin.outerToInnerWidthDifferenceInCSSPixels;
461
462
463
464
465
466
467
468
    docElt.setAttribute("height", height);
    docElt.setAttribute("width", width);
  } else {
    // Setting the size of the window in the features string instead of here
    // causes the window to grow by the size of the titlebar.
    win.resizeTo(width, height);
  }

469
470
471
472
  // Set this before showing the window so that graphics code can use it to
  // decide to skip some expensive code paths (eg. starting the GPU process).
  docElt.setAttribute("windowtype", "navigator:blank");

473
474
475
  // The window becomes visible after OnStopRequest, so make this happen now.
  win.stop();

476
477
478
  let { TelemetryTimestamps } = ChromeUtils.import(
    "resource://gre/modules/TelemetryTimestamps.jsm"
  );
479
  TelemetryTimestamps.add("blankWindowShown");
480
481
})();

482
XPCOMUtils.defineLazyServiceGetters(this, {
483
484
485
486
  aboutNewTabService: [
    "@mozilla.org/browser/aboutnewtab-service;1",
    "nsIAboutNewTabService",
  ],
487
});
488
489
490
491
XPCOMUtils.defineLazyGetter(
  this,
  "WeaveService",
  () => Cc["@mozilla.org/weave/service;1"].getService().wrappedJSObject
492
);
493

494
// lazy module getters
495

496
XPCOMUtils.defineLazyModuleGetters(this, {
497
498
499
500
501
502
  AboutNetErrorHandler:
    "resource:///modules/aboutpages/AboutNetErrorHandler.jsm",
  AboutPrivateBrowsingHandler:
    "resource:///modules/aboutpages/AboutPrivateBrowsingHandler.jsm",
  AboutProtectionsHandler:
    "resource:///modules/aboutpages/AboutProtectionsHandler.jsm",
503
504
505
506
  AddonManager: "resource://gre/modules/AddonManager.jsm",
  AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.jsm",
  AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
  AutoCompletePopup: "resource://gre/modules/AutoCompletePopup.jsm",
507
  Blocklist: "resource://gre/modules/Blocklist.jsm",
508
509
510
  BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.jsm",
  BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.jsm",
  BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
511
  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
512
513
  ContextualIdentityService:
    "resource://gre/modules/ContextualIdentityService.jsm",
514
  Corroborate: "resource://gre/modules/Corroborate.jsm",
515
  Discovery: "resource:///modules/Discovery.jsm",
516
  ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
517
  FirefoxMonitor: "resource:///modules/FirefoxMonitor.jsm",
518
  FxAccounts: "resource://gre/modules/FxAccounts.jsm",
519
  HomePage: "resource:///modules/HomePage.jsm",
520
  HybridContentTelemetry: "resource://gre/modules/HybridContentTelemetry.jsm",
521
  Integration: "resource://gre/modules/Integration.jsm",
522
  LiveBookmarkMigrator: "resource:///modules/LiveBookmarkMigrator.jsm",
523
  NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
524
  Normandy: "resource://normandy/Normandy.jsm",
525
  ObjectUtils: "resource://gre/modules/ObjectUtils.jsm",
526
527
528
529
530
  OS: "resource://gre/modules/osfile.jsm",
  PageActions: "resource:///modules/PageActions.jsm",
  PageThumbs: "resource://gre/modules/PageThumbs.jsm",
  PdfJs: "resource://pdf.js/PdfJs.jsm",
  PermissionUI: "resource:///modules/PermissionUI.jsm",
531
  PingCentre: "resource:///modules/PingCentre.jsm",
532
533
534
535
536
  PlacesBackups: "resource://gre/modules/PlacesBackups.jsm",
  PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
  PluralForm: "resource://gre/modules/PluralForm.jsm",
  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
  ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
537
  RemoteSettings: "resource://services-settings/remote-settings.js",
538
539
  RemoteSecuritySettings:
    "resource://gre/modules/psm/RemoteSecuritySettings.jsm",
540
  RFPHelper: "resource://gre/modules/RFPHelper.jsm",
541
  SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
542
  Sanitizer: "resource:///modules/Sanitizer.jsm",
543
  SaveToPocket: "chrome://pocket/content/SaveToPocket.jsm",
544
  SearchTelemetry: "resource:///modules/SearchTelemetry.jsm",
545
  SessionStartup: "resource:///modules/sessionstore/SessionStartup.jsm",
546
547
548
  SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
  ShellService: "resource:///modules/ShellService.jsm",
  TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
549
  TabUnloader: "resource:///modules/TabUnloader.jsm",
550
551
552
553
554
  UIState: "resource://services-sync/UIState.jsm",
  UITour: "resource:///modules/UITour.jsm",
  WebChannel: "resource://gre/modules/WebChannel.jsm",
  WindowsRegistry: "resource://gre/modules/WindowsRegistry.jsm",
});
555

556
557
// eslint-disable-next-line no-unused-vars
XPCOMUtils.defineLazyModuleGetters(this, {
558
  AboutLoginsParent: "resource:///modules/AboutLoginsParent.jsm",
559
560
561
  AsyncPrefs: "resource://gre/modules/AsyncPrefs.jsm",
  ContentClick: "resource:///modules/ContentClick.jsm",
  LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
562
  PluginManager: "resource:///actors/PluginParent.jsm",
563
564
565
566
  PictureInPicture: "resource://gre/modules/PictureInPicture.jsm",
  ReaderParent: "resource:///modules/ReaderParent.jsm",
});

567
568
569
570
571
/**
 * IF YOU ADD OR REMOVE FROM THIS LIST, PLEASE UPDATE THE LIST ABOVE AS WELL.
 * XXX Bug 1325373 is for making eslint detect these automatically.
 */

572
573
let initializedModules = {};

574
[
575
576
577
578
579
  [
    "ContentPrefServiceParent",
    "resource://gre/modules/ContentPrefServiceParent.jsm",
    "alwaysInit",
  ],
580
  ["ContentSearch", "resource:///modules/ContentSearch.jsm", "init"],
581
  ["UpdateListener", "resource://gre/modules/UpdateListener.jsm", "init"],
582
583
  ["webrtcUI", "resource:///modules/webrtcUI.jsm", "init"],
].forEach(([name, resource, init]) => {
584
  XPCOMUtils.defineLazyGetter(this, name, () => {
585
    ChromeUtils.import(resource, initializedModules);
586
587
588
    initializedModules[name][init]();
    return initializedModules[name];
  });
589
});
590

591
if (AppConstants.MOZ_CRASHREPORTER) {
592
593
594
  XPCOMUtils.defineLazyModuleGetters(this, {
    UnsubmittedCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
  });
595
}
596

597
XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
598
599
600
  return Services.strings.createBundle(
    "chrome://branding/locale/brand.properties"
  );
601
602
603
});

XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
604
605
606
  return Services.strings.createBundle(
    "chrome://browser/locale/browser.properties"
  );
607
608
});

609
XPCOMUtils.defineLazyGetter(this, "gTabbrowserBundle", function() {
610
611
612
  return Services.strings.createBundle(
    "chrome://browser/locale/tabbrowser.properties"
  );
613
614
});

615
616
617
const global = this;

const listeners = {
618
619
620
621
622
  observers: {
    "update-staged": ["UpdateListener"],
    "update-downloaded": ["UpdateListener"],
    "update-available": ["UpdateListener"],
    "update-error": ["UpdateListener"],
623
624
    "gmp-plugin-crash": ["PluginManager"],
    "plugin-crashed": ["PluginManager"],
625
626
  },

627
  ppmm: {
628
629
630
631
632
    // PLEASE KEEP THIS LIST IN SYNC WITH THE LISTENERS ADDED IN ContentPrefServiceParent.init
    "ContentPrefs:FunctionCall": ["ContentPrefServiceParent"],
    "ContentPrefs:AddObserverForName": ["ContentPrefServiceParent"],
    "ContentPrefs:RemoveObserverForName": ["ContentPrefServiceParent"],
    // PLEASE KEEP THIS LIST IN SYNC WITH THE LISTENERS ADDED IN ContentPrefServiceParent.init
633
634
635
636
637
638

    // PLEASE KEEP THIS LIST IN SYNC WITH THE LISTENERS ADDED IN AsyncPrefs.init
    "AsyncPrefs:SetPref": ["AsyncPrefs"],
    "AsyncPrefs:ResetPref": ["AsyncPrefs"],
    // PLEASE KEEP THIS LIST IN SYNC WITH THE LISTENERS ADDED IN AsyncPrefs.init

639
640
641
642
643
    "webrtc:UpdateGlobalIndicators": ["webrtcUI"],
    "webrtc:UpdatingIndicators": ["webrtcUI"],
  },

  mm: {
644
    "AboutLogins:CreateLogin": ["AboutLoginsParent"],
645
    "AboutLogins:DeleteLogin": ["AboutLoginsParent"],
646
    "AboutLogins:DismissBreachAlert": ["AboutLoginsParent"],
647
    "AboutLogins:HideFooter": ["AboutLoginsParent"],
648
    "AboutLogins:Import": ["AboutLoginsParent"],
649
    "AboutLogins:MasterPasswordRequest": ["AboutLoginsParent"],
650
    "AboutLogins:OpenFAQ": ["AboutLoginsParent"],
651
    "AboutLogins:OpenFeedback": ["AboutLoginsParent"],
652
    "AboutLogins:OpenPreferences": ["AboutLoginsParent"],
653
654
    "AboutLogins:OpenMobileAndroid": ["AboutLoginsParent"],
    "AboutLogins:OpenMobileIos": ["AboutLoginsParent"],
655
    "AboutLogins:OpenSite": ["AboutLoginsParent"],
656
    "AboutLogins:Subscribe": ["AboutLoginsParent"],
657
658
    "AboutLogins:SyncEnable": ["AboutLoginsParent"],
    "AboutLogins:SyncOptions": ["AboutLoginsParent"],
659
    "AboutLogins:UpdateLogin": ["AboutLoginsParent"],
660
    "Content:Click": ["ContentClick"],
661
    ContentSearch: ["ContentSearch"],
662
663
    "PictureInPicture:Request": ["PictureInPicture"],
    "PictureInPicture:Close": ["PictureInPicture"],
664
665
    "PictureInPicture:Playing": ["PictureInPicture"],
    "PictureInPicture:Paused": ["PictureInPicture"],
666
    "PictureInPicture:OpenToggleContextMenu": ["PictureInPicture"],
667
668
    "Reader:FaviconRequest": ["ReaderParent"],
    "Reader:UpdateReaderButton": ["ReaderParent"],
669
    // PLEASE KEEP THIS LIST IN SYNC WITH THE MOBILE LISTENERS IN BrowserCLH.js
670
671
672
    "PasswordManager:findLogins": ["LoginManagerParent"],
    "PasswordManager:findRecipes": ["LoginManagerParent"],
    "PasswordManager:onFormSubmit": ["LoginManagerParent"],
673
    "PasswordManager:onGeneratedPasswordFilledOrEdited": ["LoginManagerParent"],
674
675
676
    "PasswordManager:autoCompleteLogins": ["LoginManagerParent"],
    "PasswordManager:removeLogin": ["LoginManagerParent"],
    "PasswordManager:insecureLoginFormPresent": ["LoginManagerParent"],
677
    "PasswordManager:OpenPreferences": ["LoginManagerParent"],
678
    // PLEASE KEEP THIS LIST IN SYNC WITH THE MOBILE LISTENERS IN BrowserCLH.js
679
680
681
682
683
684
685
686
    "rtcpeer:CancelRequest": ["webrtcUI"],
    "rtcpeer:Request": ["webrtcUI"],
    "webrtc:CancelRequest": ["webrtcUI"],
    "webrtc:Request": ["webrtcUI"],
    "webrtc:StopRecording": ["webrtcUI"],
    "webrtc:UpdateBrowserIndicators": ["webrtcUI"],
  },

687
688
689
690
691
692
693
694
695
696
  observe(subject, topic, data) {
    for (let module of this.observers[topic]) {
      try {
        global[module].observe(subject, topic, data);
      } catch (e) {
        Cu.reportError(e);
      }
    }
  },

697
  receiveMessage(modules, data) {
698
    let val;
699
700
    for (let module of modules[data.name]) {
      try {
701
        val = global[module].receiveMessage(data) || val;
702
703
704
705
      } catch (e) {
        Cu.reportError(e);
      }
    }
706
    return val;
707
708
709
  },

  init() {
710
711
712
713
    for (let observer of Object.keys(this.observers)) {
      Services.obs.addObserver(this, observer);
    }

714
715
716
717
718
719
720
721
722
    let receiveMessageMM = this.receiveMessage.bind(this, this.mm);
    for (let message of Object.keys(this.mm)) {
      Services.mm.addMessageListener(message, receiveMessageMM);
    }

    let receiveMessagePPMM = this.receiveMessage.bind(this, this.ppmm);
    for (let message of Object.keys(this.ppmm)) {
      Services.ppmm.addMessageListener(message, receiveMessagePPMM);
    }
723
  },
724
725
};

726
// Seconds of idle before trying to create a bookmarks backup.
727
const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60;
728
729
730
731
732
// Minimum interval between backups.  We try to not create more than one backup
// per interval.
const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
// Maximum interval between backups.  If the last backup is older than these
// days we will try to create a new one more aggressively.
733
const BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS = 3;
734
735
// Seconds of idle time before the late idle tasks will be scheduled.
const LATE_TASKS_IDLE_TIME_SEC = 20;
736
737
// Time after we stop tracking startup crashes.
const STARTUP_CRASHES_END_DELAY_MS = 30 * 1000;
738

739
740
741
742
743
/*
 * OS X has the concept of zero-window sessions and therefore ignores the
 * browser-lastwindow-close-* topics.
 */
const OBSERVE_LASTWINDOW_CLOSE_TOPICS = AppConstants.platform != "macosx";
744
745

function BrowserGlue() {
746
747
748
749
750
751
  XPCOMUtils.defineLazyServiceGetter(
    this,
    "_idleService",
    "@mozilla.org/widget/idleservice;1",
    "nsIIdleService"
  );
752

753
  XPCOMUtils.defineLazyGetter(this, "_distributionCustomizer", function() {
754
755
756
757
758
    const { DistributionCustomizer } = ChromeUtils.import(
      "resource:///modules/distribution.js"
    );
    return new DistributionCustomizer();
  });
759

760
761
762
763
764
765
  XPCOMUtils.defineLazyServiceGetter(
    this,
    "AlertsService",
    "@mozilla.org/alerts-service;1",
    "nsIAlertsService"
  );
766

767
768
769
770
  this._init();
}

BrowserGlue.prototype = {
771
  _saveSession: false,
772
  _migrationImportsDefaultBookmarks: false,
773
  _placesBrowserInitComplete: false,
774

775
  _setPrefToSaveSession: function BG__setPrefToSaveSession(aForce) {
776
    if (!this._saveSession && !aForce) {
777
      return;
778
    }
779

780
    if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
781
782
783
784
      Services.prefs.setBoolPref(
        "browser.sessionstore.resume_session_once",
        true
      );
785
    }
786
787
788
789

    // This method can be called via [NSApplication terminate:] on Mac, which
    // ends up causing prefs not to be flushed to disk, so we need to do that
    // explicitly here. See bug 497652.
790
    Services.prefs.savePrefFile(null);
791
792
  },

793
794
795
  _setSyncAutoconnectDelay: function BG__setSyncAutoconnectDelay() {
    // Assume that a non-zero value for services.sync.autoconnectDelay should override
    if (Services.prefs.prefHasUserValue("services.sync.autoconnectDelay")) {
796
797
798
      let prefDelay = Services.prefs.getIntPref(
        "services.sync.autoconnectDelay"
      );
799

800
      if (prefDelay > 0) {
801
        return;
802
      }
803
804
805
806
807
    }

    // delays are in seconds
    const MAX_DELAY = 300;
    let delay = 3;
808
    for (let win of Services.wm.getEnumerator("navigator:browser")) {
809
810
811
812
813
      // browser windows without a gBrowser almost certainly means we are
      // shutting down, so instead of just ignoring that window we abort.
      if (win.closed || !win.gBrowser) {
        return;
      }
814
      delay += win.gBrowser.tabs.length;
815
816
817
    }
    delay = delay <= MAX_DELAY ? delay : MAX_DELAY;

818
    const { Weave } = ChromeUtils.import("resource://services-sync/main.js");
819
    Weave.Service.scheduler.delayedAutoConnect(delay);
820
821
  },

822
823
824
825
826
827
  /**
   * Lazily initialize PingCentre
   */
  get pingCentre() {
    const MAIN_TOPIC_ID = "main";
    Object.defineProperty(this, "pingCentre", {
828
      value: new PingCentre({ topic: MAIN_TOPIC_ID }),
829
830
831
832
833
    });
    return this.pingCentre;
  },

  _sendMainPingCentrePing() {
834
835
836
837
838
839
840
841
842
843
844
845
846
847
    let newTabSetting;
    let homePageSetting;

    // Check whether or not about:home and about:newtab have been overridden at this point.
    // Different settings are encoded as follows:
    //   * Value 0: default
    //   * Value 1: about:blank
    //   * Value 2: web extension
    //   * Value 3: other custom URL(s)
    // Settings for about:newtab and about:home are combined in a bitwise manner.

    // Note that a user could use about:blank and web extension at the same time
    // to overwrite the about:newtab, but the web extension takes priority, so the
    // ordering matters in the following check.
848
849
850
851
    if (
      Services.prefs.getBoolPref("browser.newtabpage.enabled") &&
      !aboutNewTabService.overridden
    ) {
852
853
854
855
856
857
858
859
860
      newTabSetting = 0;
    } else if (aboutNewTabService.newTabURL.startsWith("moz-extension://")) {
      newTabSetting = 2;
    } else if (!Services.prefs.getBoolPref("browser.newtabpage.enabled")) {
      newTabSetting = 1;
    } else {
      newTabSetting = 3;
    }

861
    const homePageURL = HomePage.get();
862
863
864
865
866
867
868
869
870
871
    if (homePageURL === "about:home") {
      homePageSetting = 0;
    } else if (homePageURL === "about:blank") {
      homePageSetting = 1;
    } else if (homePageURL.startsWith("moz-extension://")) {
      homePageSetting = 2;
    } else {
      homePageSetting = 3;
    }

872
873
    const payload = {
      event: "AS_ENABLED",
874
      value: newTabSetting | (homePageSetting << 2),
875
    };
876
    const ACTIVITY_STREAM_ID = "activity-stream";
877
    const options = { filter: ACTIVITY_STREAM_ID };
878
879
880
    this.pingCentre.sendPing(payload, options);
  },

881
  // nsIObserver implementation
882
  observe: async function BG_observe(subject, topic, data) {
883
    switch (topic) {
884
      case "notifications-open-settings":
885
        this._openPreferences("privacy-permissions");
886
        break;
887
      case "final-ui-startup":
888
        this._beforeUIStartup();
889
        break;
890
      case "browser-delayed-startup-finished":
891
        this._onFirstWindowLoaded(subject);
892
893
        Services.obs.removeObserver(this, "browser-delayed-startup-finished");
        break;
894
      case "sessionstore-windows-restored":
895
        this._onWindowsRestored();
896
        break;
897
898
      case "browser:purge-session-history":
        // reset the console service's error buffer
899
900
        Services.console.logStringMessage(null); // clear the console (in case it's open)
        Services.console.reset();
901
        break;
902
903
904
      case "restart-in-safe-mode":
        this._onSafeModeRestart();
        break;
905
      case "quit-application-requested":
906
        this._onQuitRequest(subject, data);
907
908
        break;
      case "quit-application-granted":
909
        this._onQuitApplicationGranted();
910
        break;
911
      case "browser-lastwindow-close-requested":
912
913
914
915
916
        if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
          // The application is not actually quitting, but the last full browser
          // window is about to be closed.
          this._onQuitRequest(subject, "lastwindow");
        }
917
918
        break;
      case "browser-lastwindow-close-granted":
919
920
921
        if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
          this._setPrefToSaveSession();
        }
922
        break;
923
924
925
      case "weave:service:ready":
        this._setSyncAutoconnectDelay();
        break;
926
927
928
      case "fxaccounts:onverified":
        this._showSyncStartedDoorhanger();
        break;
929
930
931
      case "fxaccounts:device_connected":
        this._onDeviceConnected(data);
        break;
932
933
934
      case "fxaccounts:verify_login":
        this._onVerifyLoginNotification(JSON.parse(data));
        break;
935
      case "fxaccounts:device_disconnected":
936
937
938
939
        data = JSON.parse(data);
        if (data.isLocalDevice) {
          this._onDeviceDisconnected();
        }
940
        break;
941
      case "fxaccounts:commands:open-uri":
942
943
      case "weave:engine:clients:display-uris":
        this._onDisplaySyncURIs(subject);
944
        break;
945
      case "session-save":
946
        this._setPrefToSaveSession(true);
947
948
949
        subject.QueryInterface(Ci.nsISupportsPRBool);
        subject.data = true;
        break;
950
      case "places-init-complete":
951
        Services.obs.removeObserver(this, "places-init-complete");
952
        if (!this._migrationImportsDefaultBookmarks) {
953
          this._initPlaces(false);
954
        }
955
        break;
956
      case "idle":
957
        this._backupBookmarks();
958
        break;
959
      case "distribution-customization-complete":
960
961
962
963
        Services.obs.removeObserver(
          this,
          "distribution-customization-complete"
        );
964
965
966
        // Customization has finished, we don't need the customizer anymore.
        delete this._distributionCustomizer;
        break;
967
      case "browser-glue-test": // used by tests
968
        if (data == "force-ui-migration") {
969
          this._migrateUI();
970
        } else if (data == "force-distribution-customization") {
971
          this._distributionCustomizer.applyCustomizations();
972
          // To apply distribution bookmarks use "places-init-complete".
973
        } else if (data == "force-places-init") {
974
          this._initPlaces(false);
975
976
        } else if (data == "mock-alerts-service") {
          Object.defineProperty(this, "AlertsService", {
977
            value: subject.wrappedJSObject,
978
          });
979
980
981
982
        } else if (data == "places-browser-init-complete") {
          if (this._placesBrowserInitComplete) {
            Services.obs.notifyObservers(null, "places-browser-init-complete");
          }
983
984
        } else if (data == "migrateMatchBucketsPrefForUI66") {
          this._migrateMatchBucketsPrefForUI66().then(() => {
985
986
987
988
989
            Services.obs.notifyObservers(
              null,
              "browser-glue-test",
              "migrateMatchBucketsPrefForUI66-done"
            );
990
          });
991
        }
992
        break;
993
994
995
996
997
      case "initial-migration-will-import-default-bookmarks":
        this._migrationImportsDefaultBookmarks = true;
        break;
      case "initial-migration-did-import-default-bookmarks":
        this._initPlaces(true);
998
        break;
999
1000
      case "handle-xul-text-link":
        let linkHandled = subject.QueryInterface(Ci.nsISupportsPRBool);
For faster browsing, not all history is shown. View entire blame