BrowserGlue.jsm 132 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
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");
12

13
14
15
ChromeUtils.defineModuleGetter(this, "ActorManagerParent",
                               "resource://gre/modules/ActorManagerParent.jsm");

16
17
const PREF_PDFJS_ENABLED_CACHE_STATE = "pdfjs.enabledCache.state";

18
let ACTORS = {
19
20
21
22
23
24
25
  AboutReader: {
    child: {
      module: "resource:///actors/AboutReaderChild.jsm",
      group: "browsers",
      events: {
        "AboutReaderContentLoaded": {wantUntrusted: true},
        "DOMContentLoaded": {},
26
27
        "pageshow": {mozSystemGroup: true},
        "pagehide": {mozSystemGroup: true},
28
29
30
31
32
33
34
35
      },
      messages: [
        "Reader:ToggleReaderMode",
        "Reader:PushState",
      ],
    },
  },

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  BlockedSite: {
    child: {
      module: "resource:///actors/BlockedSiteChild.jsm",
      events: {
        "AboutBlockedLoaded": {wantUntrusted: true},
        "click": {},
      },
      matches: ["about:blocked?*"],
      allFrames: true,
      messages: [
        "DeceptiveBlockedDetails",
      ],
    },
  },

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  BrowserTab: {
    child: {
      module: "resource:///actors/BrowserTabChild.jsm",
      group: "browsers",

      events: {
        "DOMWindowCreated": {once: true},
        "MozAfterPaint": {once: true},
        "MozDOMPointerLock:Entered": {},
        "MozDOMPointerLock:Exited": {},
      },

      messages: [
        "AllowScriptsToClose",
        "Browser:AppTab",
        "Browser:HasSiblings",
        "Browser:Reload",
        "MixedContent:ReenableProtection",
        "SwitchDocumentDirection",
        "UpdateCharacterSet",
      ],
    },
  },
74

75
76
77
78
79
  ClickHandler: {
    child: {
      module: "resource:///actors/ClickHandlerChild.jsm",
      events: {
        "click": {capture: true, mozSystemGroup: true},
80
      },
81
82
83
    },
  },

84
85
86
87
88
89
90
91
92
  ContextMenu: {
    child: {
      module: "resource:///actors/ContextMenuChild.jsm",
      events: {
        "contextmenu": {mozSystemGroup: true},
      },
    },
  },

93
94
95
96
  ContentSearch: {
    child: {
      module: "resource:///actors/ContentSearchChild.jsm",
      group: "browsers",
97
      matches: ["about:home", "about:newtab", "about:welcome", "about:privatebrowsing",
98
99
100
101
102
103
                "chrome://mochitests/content/*"],
      events: {
        "ContentSearchClient": {capture: true, wantUntrusted: true},
      },
      messages: [
        "ContentSearch",
104
      ],
105
106
107
    },
  },

108
109
110
111
112
113
114
115
116
117
118
119
120
121
  DOMFullscreen: {
    child: {
      module: "resource:///actors/DOMFullscreenChild.jsm",
      group: "browsers",
      events: {
        "MozDOMFullscreen:Request": {},
        "MozDOMFullscreen:Entered": {},
        "MozDOMFullscreen:NewOrigin": {},
        "MozDOMFullscreen:Exit": {},
        "MozDOMFullscreen:Exited": {},
      },
      messages: [
        "DOMFullscreen:Entered",
        "DOMFullscreen:CleanUp",
122
      ],
123
124
125
    },
  },

126
  FormValidation: {
127
    child: {
128
      module: "resource:///actors/FormValidationChild.jsm",
129
130
131
132
133
134
      events: {
        "MozInvalidForm": {},
      },
    },
  },

135
136
137
138
139
140
141
142
143
144
145
  LightWeightThemeInstall: {
    child: {
      module: "resource:///actors/LightWeightThemeInstallChild.jsm",
      events: {
        "InstallBrowserTheme": {wantUntrusted: true},
        "PreviewBrowserTheme": {wantUntrusted: true},
        "ResetBrowserThemePreview": {wantUntrusted: true},
      },
    },
  },

146
147
148
149
150
151
152
153
154
155
  LightweightTheme: {
    child: {
      module: "resource:///actors/LightweightThemeChild.jsm",
      matches: ["about:home", "about:newtab", "about:welcome"],
      events: {
        "pageshow": {mozSystemGroup: true},
      },
    },
  },

156
157
158
159
160
161
162
163
164
165
166
167
168
  LinkHandler: {
    child: {
      module: "resource:///actors/LinkHandlerChild.jsm",
      events: {
        "DOMHeadElementParsed": {},
        "DOMLinkAdded": {},
        "DOMLinkChanged": {},
        "pageshow": {},
        "pagehide": {},
      },
    },
  },

169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
  NetError: {
    child: {
      module: "resource:///actors/NetErrorChild.jsm",
      events: {
        "AboutNetErrorLoad": {wantUntrusted: true},
        "AboutNetErrorOpenCaptivePortal": {wantUntrusted: true},
        "AboutNetErrorSetAutomatic": {wantUntrusted: true},
        "AboutNetErrorResetPreferences": {wantUntrusted: true},
        "click": {},
      },
      matches: ["about:certerror?*", "about:neterror?*"],
      allFrames: true,
      messages: [
        "Browser:CaptivePortalFreed",
        "CertErrorDetails",
      ],
    },
  },

188
189
190
191
192
193
194
195
196
197
198
199
  OfflineApps: {
    child: {
      module: "resource:///actors/OfflineAppsChild.jsm",
      events: {
        "MozApplicationManifest": {},
      },
      messages: [
        "OfflineApps:StartFetching",
      ],
    },
  },

200
201
202
203
204
  PageInfo: {
    child: {
      module: "resource:///actors/PageInfoChild.jsm",
      messages: ["PageInfo:getData"],
    },
205
206
  },

207
208
209
210
211
212
213
214
215
216
  PageStyle: {
    child: {
      module: "resource:///actors/PageStyleChild.jsm",
      group: "browsers",
      events: {
        "pageshow": {},
      },
      messages: [
        "PageStyle:Switch",
        "PageStyle:Disable",
217
      ],
218
219
    },
  },
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245

  Plugin: {
    child: {
      module: "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: [
        "BrowserPlugins:ActivatePlugins",
        "BrowserPlugins:ContextMenuCommand",
        "BrowserPlugins:NPAPIPluginProcessCrashed",
        "BrowserPlugins:CrashReportSubmitted",
        "BrowserPlugins:Test:ClearCrashData",
      ],

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

247
248
249
250
251
252
253
254
255
256
  RFPHelper: {
    child: {
      module: "resource:///actors/RFPHelperChild.jsm",
      group: "browsers",
      events: {
        "resize": {},
      },
    },
  },

257
258
259
260
261
262
263
264
265
266
  SearchTelemetry: {
    child: {
      module: "resource:///actors/SearchTelemetryChild.jsm",
      events: {
        DOMContentLoaded: {},
        "pageshow": {mozSystemGroup: true},
      },
    },
  },

267
268
269
270
271
272
273
274
275
276
  ShieldFrame: {
    child: {
      module: "resource://normandy-content/ShieldFrameChild.jsm",
      events: {
        "ShieldPageEvent": {wantUntrusted: true},
      },
      matches: ["about:studies"],
    },
  },

277
278
279
280
281
282
283
284
285
286
  UITour: {
    child: {
      module: "resource:///modules/UITourChild.jsm",
      events: {
        "mozUITour": {wantUntrusted: true},
      },
      permissions: ["uitour"],
    },
  },

287
288
289
290
291
292
293
  URIFixup: {
    child: {
      module: "resource:///actors/URIFixupChild.jsm",
      group: "browsers",
      observers: ["keyword-uri-fixup"],
    },
  },
294
295
296
297
298
299
300
301
302
303
304
305
306

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

309
310
311
312
(function earlyBlankFirstPaint() {
  if (!Services.prefs.getBoolPref("browser.startup.blankWindow", false))
    return;

313
314
315
316
317
318
  // Until bug 1450626 and bug 1488384 are fixed, skip the blank window when
  // using a non-default theme.
  if (Services.prefs.getCharPref("lightweightThemes.selectedThemeID", "") !=
        "default-theme@mozilla.org")
    return;

319
  let store = Services.xulStore;
320
  let getValue = attr =>
321
    store.getValue(AppConstants.BROWSER_CHROME_URL, "main-window", attr);
322
323
324
325
326
327
328
329
330
  let width = getValue("width");
  let height = getValue("height");

  // The clean profile case isn't handled yet. Return early for now.
  if (!width || !height)
    return;

  let browserWindowFeatures =
    "chrome,all,dialog=no,extrachrome,menubar,resizable,scrollbars,status," +
331
    "location,toolbar,personalbar";
332
333
334
335
  let win = Services.ww.openWindow(null, "about:blank", null,
                                   browserWindowFeatures, null);

  // Hide the titlebar if the actual browser window will draw in it.
336
337
338
339
  let hiddenTitlebar =
    Services.prefs.getBoolPref("browser.tabs.drawInTitlebar",
      win.matchMedia("(-moz-gtk-csd-hide-titlebar-by-default)").matches);
  if (hiddenTitlebar) {
340
    win.windowUtils.setChromeMargin(0, 2, 2, 2);
341
342
  }

343
344
345
  let docElt = win.document.documentElement;
  docElt.setAttribute("screenX", getValue("screenX"));
  docElt.setAttribute("screenY", getValue("screenY"));
346
347
348
349
350
351

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

352
353
354
355
356
357
358
359
    // 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
                    .QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIXULWindow);
    height -= xulWin.outerToInnerHeightDifferenceInCSSPixels;
    width -= xulWin.outerToInnerWidthDifferenceInCSSPixels;
360
361
362
363
364
365
366
367
    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);
  }

368
369
370
371
  // 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");

372
373
374
  // The window becomes visible after OnStopRequest, so make this happen now.
  win.stop();

375
  let { TelemetryTimestamps } =
376
    ChromeUtils.import("resource://gre/modules/TelemetryTimestamps.jsm");
377
  TelemetryTimestamps.add("blankWindowShown");
378
379
})();

380
XPCOMUtils.defineLazyServiceGetters(this, {
381
  aboutNewTabService: ["@mozilla.org/browser/aboutnewtab-service;1", "nsIAboutNewTabService"],
382
});
383
384
385
XPCOMUtils.defineLazyGetter(this, "WeaveService", () =>
  Cc["@mozilla.org/weave/service;1"].getService().wrappedJSObject
);
386

387
// lazy module getters
388

389
XPCOMUtils.defineLazyModuleGetters(this, {
390
  AboutPrivateBrowsingHandler: "resource:///modules/aboutpages/AboutPrivateBrowsingHandler.jsm",
391
392
393
394
  AddonManager: "resource://gre/modules/AddonManager.jsm",
  AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.jsm",
  AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
  AutoCompletePopup: "resource://gre/modules/AutoCompletePopup.jsm",
395
  Blocklist: "resource://gre/modules/Blocklist.jsm",
396
397
398
  BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.jsm",
  BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.jsm",
  BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
399
  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
400
  ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
401
  DateTimePickerParent: "resource://gre/modules/DateTimePickerParent.jsm",
402
  Discovery: "resource:///modules/Discovery.jsm",
403
  ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
404
  FileSource: "resource://gre/modules/L10nRegistry.jsm",
405
  FxAccounts: "resource://gre/modules/FxAccounts.jsm",
406
  HomePage: "resource:///modules/HomePage.jsm",
407
  HybridContentTelemetry: "resource://gre/modules/HybridContentTelemetry.jsm",
408
  Integration: "resource://gre/modules/Integration.jsm",
409
  L10nRegistry: "resource://gre/modules/L10nRegistry.jsm",
410
  LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
411
  LiveBookmarkMigrator: "resource:///modules/LiveBookmarkMigrator.jsm",
412
  NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
413
  Normandy: "resource://normandy/Normandy.jsm",
414
  ObjectUtils: "resource://gre/modules/ObjectUtils.jsm",
415
416
417
418
419
  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",
420
  PingCentre: "resource:///modules/PingCentre.jsm",
421
422
423
424
425
  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",
426
  RemoteSettings: "resource://services-settings/remote-settings.js",
427
  RFPHelper: "resource://gre/modules/RFPHelper.jsm",
428
  SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
429
  Sanitizer: "resource:///modules/Sanitizer.jsm",
430
  SaveToPocket: "chrome://pocket/content/SaveToPocket.jsm",
431
  SearchTelemetry: "resource:///modules/SearchTelemetry.jsm",
432
  SessionStartup: "resource:///modules/sessionstore/SessionStartup.jsm",
433
434
435
436
437
438
439
440
  SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
  ShellService: "resource:///modules/ShellService.jsm",
  TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
  UIState: "resource://services-sync/UIState.jsm",
  UITour: "resource:///modules/UITour.jsm",
  WebChannel: "resource://gre/modules/WebChannel.jsm",
  WindowsRegistry: "resource://gre/modules/WindowsRegistry.jsm",
});
441

442
443
444
445
446
447
448
449
450
451
452
// eslint-disable-next-line no-unused-vars
XPCOMUtils.defineLazyModuleGetters(this, {
  AsyncPrefs: "resource://gre/modules/AsyncPrefs.jsm",
  ContentClick: "resource:///modules/ContentClick.jsm",
  FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
  LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
  PictureInPicture: "resource://gre/modules/PictureInPicture.jsm",
  ReaderParent: "resource:///modules/ReaderParent.jsm",
  RemotePrompt: "resource:///modules/RemotePrompt.jsm",
});

453
/* global ContentPrefServiceParent:false, ContentSearch:false,
454
          UpdateListener:false, webrtcUI:false */
455

456
457
458
459
460
/**
 * 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.
 */

461
462
let initializedModules = {};

463
[
464
  ["ContentPrefServiceParent", "resource://gre/modules/ContentPrefServiceParent.jsm", "alwaysInit"],
465
  ["ContentSearch", "resource:///modules/ContentSearch.jsm", "init"],
466
  ["UpdateListener", "resource://gre/modules/UpdateListener.jsm", "init"],
467
468
  ["webrtcUI", "resource:///modules/webrtcUI.jsm", "init"],
].forEach(([name, resource, init]) => {
469
  XPCOMUtils.defineLazyGetter(this, name, () => {
470
    ChromeUtils.import(resource, initializedModules);
471
472
473
    initializedModules[name][init]();
    return initializedModules[name];
  });
474
});
475

476
if (AppConstants.MOZ_CRASHREPORTER) {
477
478
479
480
  XPCOMUtils.defineLazyModuleGetters(this, {
    PluginCrashReporter: "resource:///modules/ContentCrashHandlers.jsm",
    UnsubmittedCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
  });
481
}
482

483
XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
484
  return Services.strings.createBundle("chrome://branding/locale/brand.properties");
485
486
487
});

XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
488
  return Services.strings.createBundle("chrome://browser/locale/browser.properties");
489
490
});

491
492
493
494
XPCOMUtils.defineLazyGetter(this, "gTabbrowserBundle", function() {
  return Services.strings.createBundle("chrome://browser/locale/tabbrowser.properties");
});

495
496
497
const global = this;

const listeners = {
498
499
500
501
502
503
504
  observers: {
    "update-staged": ["UpdateListener"],
    "update-downloaded": ["UpdateListener"],
    "update-available": ["UpdateListener"],
    "update-error": ["UpdateListener"],
  },

505
  ppmm: {
506
507
508
509
510
    // 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
511
512
513
514
515
516

    // 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

517
518
519
520
521
    "webrtc:UpdateGlobalIndicators": ["webrtcUI"],
    "webrtc:UpdatingIndicators": ["webrtcUI"],
  },

  mm: {
522
    "Content:Click": ["ContentClick"],
523
    "ContentSearch": ["ContentSearch"],
524
525
    "FormValidation:ShowPopup": ["FormValidationHandler"],
    "FormValidation:HidePopup": ["FormValidationHandler"],
526
527
    "PictureInPicture:Request": ["PictureInPicture"],
    "PictureInPicture:Close": ["PictureInPicture"],
528
    "Prompt:Open": ["RemotePrompt"],
529
530
    "Reader:FaviconRequest": ["ReaderParent"],
    "Reader:UpdateReaderButton": ["ReaderParent"],
531
    // PLEASE KEEP THIS LIST IN SYNC WITH THE MOBILE LISTENERS IN BrowserCLH.js
532
533
534
535
536
537
    "RemoteLogins:findLogins": ["LoginManagerParent"],
    "RemoteLogins:findRecipes": ["LoginManagerParent"],
    "RemoteLogins:onFormSubmit": ["LoginManagerParent"],
    "RemoteLogins:autoCompleteLogins": ["LoginManagerParent"],
    "RemoteLogins:removeLogin": ["LoginManagerParent"],
    "RemoteLogins:insecureLoginFormPresent": ["LoginManagerParent"],
538
    // PLEASE KEEP THIS LIST IN SYNC WITH THE MOBILE LISTENERS IN BrowserCLH.js
539
540
541
542
543
544
545
546
    "rtcpeer:CancelRequest": ["webrtcUI"],
    "rtcpeer:Request": ["webrtcUI"],
    "webrtc:CancelRequest": ["webrtcUI"],
    "webrtc:Request": ["webrtcUI"],
    "webrtc:StopRecording": ["webrtcUI"],
    "webrtc:UpdateBrowserIndicators": ["webrtcUI"],
  },

547
548
549
550
551
552
553
554
555
556
  observe(subject, topic, data) {
    for (let module of this.observers[topic]) {
      try {
        global[module].observe(subject, topic, data);
      } catch (e) {
        Cu.reportError(e);
      }
    }
  },

557
  receiveMessage(modules, data) {
558
    let val;
559
560
    for (let module of modules[data.name]) {
      try {
561
        val = global[module].receiveMessage(data) || val;
562
563
564
565
      } catch (e) {
        Cu.reportError(e);
      }
    }
566
    return val;
567
568
569
  },

  init() {
570
571
572
573
    for (let observer of Object.keys(this.observers)) {
      Services.obs.addObserver(this, observer);
    }

574
575
576
577
578
579
580
581
582
    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);
    }
583
  },
584
585
};

586
// Seconds of idle before trying to create a bookmarks backup.
587
const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60;
588
589
590
591
592
// 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.
593
const BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS = 3;
594
595
// Seconds of idle time before the late idle tasks will be scheduled.
const LATE_TASKS_IDLE_TIME_SEC = 20;
596
597
// Time after we stop tracking startup crashes.
const STARTUP_CRASHES_END_DELAY_MS = 30 * 1000;
598

599
600
601
602
603
/*
 * 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";
604
605

function BrowserGlue() {
606
607
608
609
  XPCOMUtils.defineLazyServiceGetter(this, "_idleService",
                                     "@mozilla.org/widget/idleservice;1",
                                     "nsIIdleService");

610
  XPCOMUtils.defineLazyGetter(this, "_distributionCustomizer", function() {
611
                                const {DistributionCustomizer} = ChromeUtils.import("resource:///modules/distribution.js");
612
613
614
                                return new DistributionCustomizer();
                              });

615
  XPCOMUtils.defineLazyServiceGetter(this, "AlertsService", "@mozilla.org/alerts-service;1", "nsIAlertsService");
616

617
618
619
620
  this._init();
}

BrowserGlue.prototype = {
621
  _saveSession: false,
622
  _migrationImportsDefaultBookmarks: false,
623
  _placesBrowserInitComplete: false,
624

625
  _setPrefToSaveSession: function BG__setPrefToSaveSession(aForce) {
626
627
628
    if (!this._saveSession && !aForce)
      return;

629
630
631
    if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
      Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
    }
632
633
634
635

    // 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.
636
    Services.prefs.savePrefFile(null);
637
638
  },

639
640
641
642
643
644
645
646
647
648
649
650
  _setSyncAutoconnectDelay: function BG__setSyncAutoconnectDelay() {
    // Assume that a non-zero value for services.sync.autoconnectDelay should override
    if (Services.prefs.prefHasUserValue("services.sync.autoconnectDelay")) {
      let prefDelay = Services.prefs.getIntPref("services.sync.autoconnectDelay");

      if (prefDelay > 0)
        return;
    }

    // delays are in seconds
    const MAX_DELAY = 300;
    let delay = 3;
651
    for (let win of Services.wm.getEnumerator("navigator:browser")) {
652
653
654
655
656
      // 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;
      }
657
      delay += win.gBrowser.tabs.length;
658
659
660
    }
    delay = delay <= MAX_DELAY ? delay : MAX_DELAY;

661
    const {Weave} = ChromeUtils.import("resource://services-sync/main.js");
662
    Weave.Service.scheduler.delayedAutoConnect(delay);
663
664
  },

665
666
667
668
669
670
  /**
   * Lazily initialize PingCentre
   */
  get pingCentre() {
    const MAIN_TOPIC_ID = "main";
    Object.defineProperty(this, "pingCentre", {
671
      value: new PingCentre({ topic: MAIN_TOPIC_ID }),
672
673
674
675
676
    });
    return this.pingCentre;
  },

  _sendMainPingCentrePing() {
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
    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.
    if (Services.prefs.getBoolPref("browser.newtabpage.enabled") &&
                                   !aboutNewTabService.overridden) {
      newTabSetting = 0;
    } else if (aboutNewTabService.newTabURL.startsWith("moz-extension://")) {
      newTabSetting = 2;
    } else if (!Services.prefs.getBoolPref("browser.newtabpage.enabled")) {
      newTabSetting = 1;
    } else {
      newTabSetting = 3;
    }

702
    const homePageURL = HomePage.get();
703
704
705
706
707
708
709
710
711
712
    if (homePageURL === "about:home") {
      homePageSetting = 0;
    } else if (homePageURL === "about:blank") {
      homePageSetting = 1;
    } else if (homePageURL.startsWith("moz-extension://")) {
      homePageSetting = 2;
    } else {
      homePageSetting = 3;
    }

713
714
    const payload = {
      event: "AS_ENABLED",
715
      value: newTabSetting | (homePageSetting << 2),
716
    };
717
    const ACTIVITY_STREAM_ID = "activity-stream";
718
719
720
721
    const options = {filter: ACTIVITY_STREAM_ID};
    this.pingCentre.sendPing(payload, options);
  },

722
  // nsIObserver implementation
723
  observe: async function BG_observe(subject, topic, data) {
724
    switch (topic) {
725
      case "notifications-open-settings":
726
        this._openPreferences("privacy-permissions", { origin: "notifOpenSettings" });
727
        break;
728
      case "final-ui-startup":
729
        this._beforeUIStartup();
730
        break;
731
      case "browser-delayed-startup-finished":
732
        this._onFirstWindowLoaded(subject);
733
734
        Services.obs.removeObserver(this, "browser-delayed-startup-finished");
        break;
735
      case "sessionstore-windows-restored":
736
        this._onWindowsRestored();
737
        break;
738
739
      case "browser:purge-session-history":
        // reset the console service's error buffer
740
741
        Services.console.logStringMessage(null); // clear the console (in case it's open)
        Services.console.reset();
742
        break;
743
744
745
      case "restart-in-safe-mode":
        this._onSafeModeRestart();
        break;
746
      case "quit-application-requested":
747
        this._onQuitRequest(subject, data);
748
749
        break;
      case "quit-application-granted":
750
        this._onQuitApplicationGranted();
751
        break;
752
      case "browser-lastwindow-close-requested":
753
754
755
756
757
        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");
        }
758
759
        break;
      case "browser-lastwindow-close-granted":
760
761
762
        if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
          this._setPrefToSaveSession();
        }
763
        break;
764
765
766
      case "weave:service:ready":
        this._setSyncAutoconnectDelay();
        break;
767
768
769
      case "fxaccounts:onverified":
        this._showSyncStartedDoorhanger();
        break;
770
771
772
      case "fxaccounts:device_connected":
        this._onDeviceConnected(data);
        break;
773
774
775
      case "fxaccounts:verify_login":
        this._onVerifyLoginNotification(JSON.parse(data));
        break;
776
      case "fxaccounts:device_disconnected":
777
778
779
780
        data = JSON.parse(data);
        if (data.isLocalDevice) {
          this._onDeviceDisconnected();
        }
781
        break;
782
      case "fxaccounts:commands:open-uri":
783
784
      case "weave:engine:clients:display-uris":
        this._onDisplaySyncURIs(subject);
785
        break;
786
      case "session-save":
787
        this._setPrefToSaveSession(true);
788
789
790
        subject.QueryInterface(Ci.nsISupportsPRBool);
        subject.data = true;
        break;
791
      case "places-init-complete":
792
        Services.obs.removeObserver(this, "places-init-complete");
793
794
        if (!this._migrationImportsDefaultBookmarks)
          this._initPlaces(false);
795
        break;
796
      case "idle":
797
        this._backupBookmarks();
798
        break;
799
      case "distribution-customization-complete":
800
        Services.obs.removeObserver(this, "distribution-customization-complete");
801
802
803
        // Customization has finished, we don't need the customizer anymore.
        delete this._distributionCustomizer;
        break;
804
805
      case "browser-glue-test": // used by tests
        if (data == "post-update-notification") {
806
          if (Services.prefs.prefHasUserValue("app.update.postupdate"))
807
            this._showUpdateNotification();
808
        } else if (data == "force-ui-migration") {
809
          this._migrateUI();
810
        } else if (data == "force-distribution-customization") {
811
          this._distributionCustomizer.applyCustomizations();
812
          // To apply distribution bookmarks use "places-init-complete".
813
        } else if (data == "force-places-init") {
814
          this._initPlaces(false);
815
816
        } else if (data == "mock-alerts-service") {
          Object.defineProperty(this, "AlertsService", {
817
            value: subject.wrappedJSObject,
818
          });
819
820
821
822
        } else if (data == "places-browser-init-complete") {
          if (this._placesBrowserInitComplete) {
            Services.obs.notifyObservers(null, "places-browser-init-complete");
          }
823
824
825
826
827
        } else if (data == "migrateMatchBucketsPrefForUI66") {
          this._migrateMatchBucketsPrefForUI66().then(() => {
            Services.obs.notifyObservers(null, "browser-glue-test",
                                         "migrateMatchBucketsPrefForUI66-done");
          });
828
        }
829
        break;
830
831
832
833
834
      case "initial-migration-will-import-default-bookmarks":
        this._migrationImportsDefaultBookmarks = true;
        break;
      case "initial-migration-did-import-default-bookmarks":
        this._initPlaces(true);
835
        break;
836
837
838
      case "handle-xul-text-link":
        let linkHandled = subject.QueryInterface(Ci.nsISupportsPRBool);
        if (!linkHandled.data) {
839
          let win = BrowserWindowTracker.getTopWindow();
840
          if (win) {
841
842
            data = JSON.parse(data);
            let where = win.whereToOpenLink(data);
843
844
845
846
847
            // Preserve legacy behavior of non-modifier left-clicks
            // opening in a new selected tab.
            if (where == "current") {
              where = "tab";
            }
848
            win.openTrustedLinkIn(data.href, where);
849
850
851
852
            linkHandled.data = true;
          }
        }
        break;
853
      case "profile-before-change":
854
855
856
857
         // Any component depending on Places should be finalized in
         // _onPlacesShutdown.  Any component that doesn't need to act after
         // the UI has gone should be finalized in _onQuitApplicationGranted.
        this._dispose();
858
        break;
859
      case "keyword-search":
860
861
        // This notification is broadcast by the docshell when it "fixes up" a
        // URI that it's been asked to load into a keyword search.
862
863
864
865
866
867
        let engine = null;
        try {
          engine = subject.QueryInterface(Ci.nsISearchEngine);
        } catch (ex) {
          Cu.reportError(ex);
        }
868
        let win = BrowserWindowTracker.getTopWindow();
869
        win.BrowserSearch.recordSearchInTelemetry(engine, "urlbar");
870
        break;
871
      case "browser-search-engine-modified":
872
873
874
875
        // Ensure we cleanup the hiddenOneOffs pref when removing
        // an engine, and that newly added engines are visible.
        if (data == "engine-added" || data == "engine-removed") {
          let engineName = subject.QueryInterface(Ci.nsISearchEngine).name;
876
          let pref = Services.prefs.getStringPref("browser.search.hiddenOneOffs");
877
878
          let hiddenList = pref ? pref.split(",") : [];
          hiddenList = hiddenList.filter(x => x !== engineName);
879
          Services.prefs.setStringPref("browser.search.hiddenOneOffs", hiddenList.join(","));
880
        }
881
        break;
882
883
884
      case "flash-plugin-hang":
        this._handleFlashHang();
        break;
885
      case "xpi-signature-changed":
886
        let disabledAddons = JSON.parse(data).disabled;
887
888
889
890
        let addons = await AddonManager.getAddonsByIDs(disabledAddons);
        if (addons.some(addon => addon)) {
          this._notifyUnsignedAddonsDisabled();
        }
891
        break;
892
893
894
      case "sync-ui-state:update":
        this._updateFxaBadges();
        break;
895
896
897
898
899
900
901
902
903
904
      case "handlersvc-store-initialized":
        // Initialize PdfJs when running in-process and remote. This only
        // happens once since PdfJs registers global hooks. If the PdfJs
        // extension is installed the init method below will be overridden
        // leaving initialization to the extension.
        // parent only: configure default prefs, set up pref observers, register
        // pdf content handler, and initializes parent side message manager
        // shim for privileged api access.
        PdfJs.init(true);
        break;
905
      case "shield-init-complete":
906
        this._shieldInitComplete = true;
907
908
        this._sendMainPingCentrePing();
        break;
909
910
911
    }
  },

912
  // initialization (called on application startup)
913
  _init: function BG__init() {
914
    let os = Services.obs;
915
916
917
918
919
920
921
    os.addObserver(this, "notifications-open-settings");
    os.addObserver(this, "final-ui-startup");
    os.addObserver(this, "browser-delayed-startup-finished");
    os.addObserver(this, "sessionstore-windows-restored");
    os.addObserver(this, "browser:purge-session-history");
    os.addObserver(this, "quit-application-requested");
    os.addObserver(this, "quit-application-granted");
922
    if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
923
924
      os.addObserver(this, "browser-lastwindow-close-requested");
      os.addObserver(this, "browser-lastwindow-close-granted");
925
    }
926
927
928
    os.addObserver(this, "weave:service:ready");
    os.addObserver(this, "fxaccounts:onverified");
    os.addObserver(this, "fxaccounts:device_connected");
929
    os.addObserver(this, "fxaccounts:verify_login");
930
    os.addObserver(this, "fxaccounts:device_disconnected");
931
    os.addObserver(this, "fxaccounts:commands:open-uri");
932
933
934
935
936
937
    os.addObserver(this, "weave:engine:clients:display-uris");
    os.addObserver(this, "session-save");
    os.addObserver(this, "places-init-complete");
    os.addObserver(this, "distribution-customization-complete");
    os.addObserver(this, "handle-xul-text-link");
    os.addObserver(this, "profile-before-change");
938
    os.addObserver(this, "keyword-search");
939
940
941
942
    os.addObserver(this, "browser-search-engine-modified");
    os.addObserver(this, "restart-in-safe-mode");
    os.addObserver(this, "flash-plugin-hang");
    os.addObserver(this, "xpi-signature-changed");
943
    os.addObserver(this, "sync-ui-state:update");
944
    os.addObserver(this, "handlersvc-store-initialized");
945
    os.addObserver(this, "shield-init-complete");
946

947
948
949
    ActorManagerParent.addActors(ACTORS);
    ActorManagerParent.flush();

950
    this._flashHangCount = 0;
951
    this._firstWindowReady = new Promise(resolve => this._firstWindowLoaded = resolve);
952
953
954
    if (AppConstants.platform == "win") {
      JawsScreenReaderVersionCheck.init();
    }
955
956
957
  },

  // cleanup (called on application shutdown)
958
  _dispose: function BG__dispose() {
959
    let os = Services.obs;
960
    os.removeObserver(this, "notifications-open-settings");
961
962
963
964
965
    os.removeObserver(this, "final-ui-startup");
    os.removeObserver(this, "sessionstore-windows-restored");
    os.removeObserver(this, "browser:purge-session-history");
    os.removeObserver(this, "quit-application-requested");
    os.removeObserver(this, "quit-application-granted");
966
    os.removeObserver(this, "restart-in-safe-mode");
967
968
969
970
    if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
      os.removeObserver(this, "browser-lastwindow-close-requested");
      os.removeObserver(this, "browser-lastwindow-close-granted");
    }
971
    os.removeObserver(this, "weave:service:ready");
972
    os.removeObserver(this, "fxaccounts:onverified");
973
    os.removeObserver(this, "fxaccounts:device_connected");
974
    os.removeObserver(this, "fxaccounts:verify_login");
975
    os.removeObserver(this, "fxaccounts:device_disconnected");
976
    os.removeObserver(this, "fxaccounts:commands:open-uri");
977
    os.removeObserver(this, "weave:engine:clients:display-uris");
978
    os.removeObserver(this, "session-save");
979
980
981
982
    if (this._bookmarksBackupIdleTime) {
      this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime);
      delete this._bookmarksBackupIdleTime;
    }
983
984
985
986
    if (this._lateTasksIdleObserver) {
      this._idleService.removeIdleObserver(this._lateTasksIdleObserver, LATE_TASKS_IDLE_TIME_SEC);
      delete this._lateTasksIdleObserver;
    }
987
988
989
990
    if (this._gmpInstallManager) {
      this._gmpInstallManager.uninit();
      delete this._gmpInstallManager;
    }
991
    try {
992
      os.removeObserver(this, "places-init-complete");
993
    } catch (ex) { /* Could have been removed already */ }
994
    os.removeObserver(this, "handle-xul-text-link");
995
    os.removeObserver(this, "profile-before-change");
996
    os.removeObserver(this, "keyword-search");
997
    os.removeObserver(this, "browser-search-engine-modified");
998
    os.removeObserver(this, "flash-plugin-hang");
999
    os.removeObserver(this, "xpi-signature-changed");
1000
    os.removeObserver(this, "sync-ui-state:update");
1001
    os.removeObserver(this, "shield-init-complete");
1002

1003
    Services.prefs.removeObserver("privacy.trackingprotection", this._matchCBCategory);
1004
1005
1006
    Services.prefs.removeObserver("urlclassifier.trackingTable", this._matchCBCategory);
    Services.prefs.removeObserver("network.cookie.cookieBehavior", this._matchCBCategory);
    Services.prefs.removeObserver(ContentBlockingCategoriesPrefs.PREF_CB_CATEGORY, this._updateCBCategory);
1007
1008
  },

1009
1010
  // runs on startup, before the first command line handler is invoked
  // (i.e. before the first window is opened)
1011
  _beforeUIStartup: function BG__beforeUIStartup() {
1012
1013
    SessionStartup.init();

1014
1015
1016
1017
1018
1019
1020
    if (Services.prefs.prefHasUserValue(PREF_PDFJS_ENABLED_CACHE_STATE)) {
      Services.ppmm.sharedData.set(
        "pdfjs.enabled",
        Services.prefs.getBoolPref(PREF_PDFJS_ENABLED_CACHE_STATE));
    } else {
      PdfJs.earlyInit();
    }
1021

1022
1023
1024
1025
1026
1027
1028
1029
    // Initialize the default l10n resource sources for L10nRegistry.
    let locales = Services.locale.packagedLocales;
    const greSource = new FileSource("toolkit", locales, "resource://gre/localization/{locale}/");
    L10nRegistry.registerSource(greSource);

    const appSource = new FileSource("app", locales, "resource://app/localization/{locale}/");
    L10nRegistry.registerSource(appSource);

1030
    // check if we're in safe mode
1031
    if (Services.appinfo.inSafeMode) {
1032
      Services.ww.openWindow(null, "chrome://browser/content/safeMode.xul",
1033
                             "_blank", "chrome,centerscreen,modal,resizable=no", null);
1034
    }
1035

1036
    // apply distribution customizations
1037
    this._distributionCustomizer.applyCustomizations();
1038

1039
    // handle any UI migration
1040
    this._migrateUI();
1041

1042
1043
    listeners.init();

1044
    SessionStore.init();
1045

1046
1047
1048
1049
1050
1051
1052
    let vendorShortName = gBrandBundle.GetStringFromName("vendorShortName");

    LightweightThemeManager.addBuiltInTheme({
      id: "firefox-compact-light@mozilla.org",
      name: gBrowserBundle.GetStringFromName("lightTheme.name"),
      description: gBrowserBundle.GetStringFromName("lightTheme.description"),
      iconURL: "resource:///chrome/browser/content/browser/defaultthemes/light.icon.svg",
1053
1054