mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
157a125630
- Bug 1144778 - Send an HTTP Date request header with telemetry pings. r=dexter (b630ff3123) - Bug 1268581 - Reduce Telemetry initialization delay in tests. r=dexter (08ea2597f4) - Bug 1205898 - Making TelemetryStopwatch.jsm handle keyed histograms. r=gfritzsche (2e1d793bc0) - Bug 1262386 - Move Scheduler shutdown() next to init(). r=dexter (9aa12c0859) - Bug 1162538 - Add test coverage for Telemetry archive probes. r=gfritzsche (f7f9095e82) - Bug 1186955 - Add test coverage. r=gfritzsche (cc76ba4fda) - Bug 1262386 - Trigger Telemetry scheduler tick after waking up from sleep. r=dexter (27b03357bf) - Bug 1178005 - Fix unintended octal literals (00 => 0). r=Mossop (dd738033b1) - Bug 1222054 - Fix broken tests. r=gfritzsche (a6c6fa3445) - Bug 1161515 - Reset UUID generator with fakeGenerateUUID(). r=gfritzsche (460ed17cb5) - Bug 1209912 - Use JSON.parse instead of nsIJSON.decode in Telemetry tests. r=Dexter (7ca55a9b5c) - Bug 1245136 - Discard old-format pending Telemetry files. r=gfritzsche (57f2a19c9d) - Bug 1034138 - d. Test BackgroundHangMonitor permahangs; r=vladan (7313b143bc) - Bug 1016629 - h. Fix wrong sysinfo usage; r=trivial (9823cd5ed0) - Bug 1197612 - In test_TelemetrySendOldPings.js, set the FHR pref to the value the tests assume it has. r=gfritzsche (06a41428b2) - Bug 1136082 - test_TelemetrySendOldPings.js is missing AddonManager initialization. r=froydnj (cfc700dd44) - Bug 1239480 - Make test_TelemetryReportingPolicy.js set the minimum policy version pref for the right channel. r=gfritzsche (e44841aa2a) - Bug 1230213 - test_TelemetryLog yields from a non generator function. r=dexter (34f28fb0b1) - Bug 1226178 - Fix deprecated octal literals warnings in Telemetry tests. r=gfritzsche (88208f0f2e) - Bug 1174111 - |test_sendTimeout| in test_TelemetryControllerShutdown.js must not wait on ping submission. r=gfritzsche (3b010e9743) - Bug 1120379 - Add tests for the deletion ping. r=gfritzsche (61e6fbd8a1) - Bug 1178262 - Add test-coverage for sending persisted deletion pings. r=gfritzsche (657fbbbcc2) - Bug 1174674 - Add test coverage. r=gfritzsche (426212e94c) - Bug 1167456 - Add test coverage. r=gfritzsche (4842d38e2e) - Bug 1198364 - Set FHR upload pref in telemetry/tests/unit/head.js to avoid test failures. r=gfritzsche (024599364e) - Bug 1149284 - Restore Telemetry tests. r=rvitillo, r=sstamm (88d4022e6b) - add esl (c459da1cd3) - Bug 1251785 - Remove remaining references to MOZILLA_XPCOMRT_API from toolkit. r=froydnj (5b5708031d) - Bug 1120379 - Add the documentation for deletion pings. r=gfritzsche (723f44f669) - Bug 1241599 - Add 'core' ping telemetry docs. r=gfritzsche (35fcc54195) - Bug 1241697 - Add docs for 'experiments' field in core ping. r=mfinkle (3c408f1a61) - Bug 1247572 - Add profileDate field to Android core ping docs. r=gfritzsche (79b1c71b53) - Bug 1249288 - Update telemetry docs to include defaultSearch. r=gfritzsche,rnewman (3807061291) - Bug 1257595 - Document that the 'core' pings profileDate field can be missing. r=mcomella (f40392cb2d) - Bug 1246816 - Add docs for profileDate. r=gfritzsche (7248d799aa) - Bug 1263761 - Update defaultSearch in core ping docs for custom search engines. r=gfritzsche (e516ecbab3) - Bug 1247982 - Lock request headers. r=mcmanus (8a5940a109) - Bug 1251332 - add PAC support for reversed ranges; r=bagder (7ac8fd97cb) - Bug 1178337 - Part 1: Supports all referrer policies for element atrributes. r=sicking, r=Gijs, r=mcmanus (85b7421b02) - Bug 1187357 - rename referrer attribute to referrerpolicy in tests. r=hsivonen (da334bf712) - Bug 1178337 - Part 2: Update/add test cases. r=sicking. (acec860fd8) - Bug 1260766 - the comparison of OriginAttributes in nsFrameLoader::Swap* methods should consider the usercontextid attribute of the owning element, r=smaug (82f8895bec) - bits of Bug 1110485 P1 (f8908eb35f) - Bug 1195968 - Check how CanvasFilterChainObserver accesses the CanvasRenderingContext2D. r=mstange (eecc12329b) - Bug 1260960 - Check skiaGL is enabled before calling skiaGLTex(), r=snorp (82c069fcc9) - align test stuff (c0ccc11ed9) - Bug 1245256 - GMP Plugins now installed in flat manner to accomodate widevine. r=spohl (ae0eb69979) - Bug 1245649: Turn on use-isnan, no-unexpected-multiline (0633b3470f) - Bug 1079665 - [e10s] Findbar focusContent doesn't work when a link was found. r=enndeakin (b349e62d5a) - Bug 1174291 - Fix ctrl-return for e10s findbar. r=evilpie (6f0bf0b149) - Bug 1174289 - Remove fake focus-ring after e10s findbar is closed. r=evilpie (302ffe7559) - Bug 1079665 - follow up, remove accidental line included from different patch. r=me (0904fe927c) - Bug 1260208 - part 1 - use C I/O facilities in TestWebGLElementArrayCache instead of C++ ones; r=BenWa The libc++ included with the Android NDK does not seem to work correctly with std::cerr; writing to it (or to std::cout, as confirmed by tests) causes the process to hang indefinitely, causing test failures. Using fprintf and stderr, however, seems to work correctly. (4279c2d09d) - Bug 1260208 - part 5 - add libc++ license to about:license when using it on Android; r=gerv,nalexander (4bcf23794a) - Bug 1260208 - part 0 - correctly compare EHTable when sorting; r=froydnj operator< for EHTable compares the LHS start PC with the RHS *end* PC. Because the ranges are non-overlapping, this works fine for two distinct EHTables. However, the comparison doesn't work if LHS and RHS refer to the same EHTable; in that case operator< returns true, even though it should return false because the two operands are identical. (dc2af36cec) - Bug 1254908 - ./mach bootstrap doesn't work on CentOS/Fedora when it attempts to upgrade Mercurial. r=gps (d288b314ec) - Bug 1239413. Clarify comment about the context flags. (c2b01ef6d1) - Bug 1146875 - Fix the leak-gauge.html broken and add a encoding declaration. r=dbaron (8df2510774) - Bug 1132499 - part 1 - convert nsTextEditorState::mValue to use Maybe instead of heap allocation; r=ehsan (2644faebbb) - Bug 1132499 - part 2 - convert nsTextEditorState::mValue to be an nsString; r=ehsan (6e0df4d1dc) - Bug 409885 - Use SetHostPort in nsHTMLDocument::SetDomain. r=bz (2d289a79c5) - Bug 1269475 - Fix uninitialized variable warning. r=jdm (881fa29803) - Bug 1263935 - Expose native version of mapURIToAddonId via amIAddonPathService. r=mossop (04da9fe703) - missing bit of Bug 1255040 Add webidl for install/uninstall (63b5489b3d) - Bug 1234974 - Handle null GetOwner() in |BluetoothAdapter::IsBluetoothCertifiedApp|, r=shuang (e4dde47d7f) - Bug 1241117 - Fix mAppUuid access before assign in BluetoothGatt::Connect. r=joliu (3920535f2a) - Bug 1238424 - Fix a missing header in Bluetooth module. r=brsun (438fd74375) - Bug 1267958: Only complain for negative size, not merely zero. r=jrmuizel (194bd2a754) - Bug 1320621 - Make sure --enable-system-hunspell picks up system headers. r=glandium (125c4ea215) - Bug 1266430: Support offsets in DrawTargetD2D1::CopySurface when using PartialUploading. r=jrmuizel (de8630b117) - Bug 1267363. Disable hinting on osx fonts only if we have expclity grayscale AA. r=mstange (c4c32fd7cc) - Bug 1134549 - Switch FlattenBezier from floats to doubles. r=bas (5753ab16a3) - Bug 1240437 part 2: Follow-up to fix implicit constrcutor static analysis failure. r=me (525d9d9a9f) - Bug 1256678 - Backout diagnostic patch rev 39c895b67af2 - r=me (f63698de77) - Bug 1256678 - Account for truncated font names in ScaledFontWin::GetFontFileData() - r=jfkthame (a6930e0a33) - Bug 1256678 - Re-add nightly-only crash if ScaledFontWin::GetFontFileData fails to find a matching TrueType collection font - r=jfkthame (8bec2d43a2) - Bug 1260350 - GFX: 2D: Use ConvolveHorizontally_LS3. r=jrmuizel (9d2f372593) - Bug 1260112 - GFX: 2D: Fix unaligned access in ConvolveVertically/Horizontally_LS3. r=huangwenjun06 (43ba8f7b05) - Bug 1260885 - Make SurfaceFormat::R5G6B5_UINT16 properly round-trip through SourceSurfaceCairo. r=lsalzman (bc8f0c0876) - Bug 1262415 - make Accessible to keep weak pointers to parent and children, r=yzen (c0b987fe10) - Bug 1267271 - skip empty surfaces in DrawTargetSkia::DrawSurfaceWithShadow. r=mchang (fd488c918a) - Bug 1249600. Lookup font and font family from font face when requesting SkTypeface. r=bas (0281313c33) - Bug 1268096. Sync the D3D11 rasterizer state if pointDrawMode or multisample changes. (25b2af8b26) - Bug 1203132 - Add an SVG for the B2G flow of touch events. r=botond (2ce3b0ddc4) - Bug 1180322. Add a document describing the history of layers (b3351dbba3) - Bug 1235223. Update history document. (212d356bd0) - Bug 1235223 - Fix typos in layers history document. (f19a5802d8) - Bug 1235223 - Fix another typo in the layers history document. (9663fe5447) - Bug 1265112 - Add support for NV_texture_barrier to GLContext. r=jrmuizel (f1f5fb1a3a) - Bug 1240730 - Initialize all GLLibraryEGL symbols in the ctor. r=mattwoodrow (dae8955458) - Bug 1240730 - Initialize all GLXLibrary members in the ctor. r=mattwoodrow (8218ed6fd9) - bug 1250485 - make the ctor for GLXLibrary constexpr r=jrmuizel (a571491f0a) - mitor spacing (0083550ab2) - Bug 1256492 - Cast to proper width to avoid C4312 on VS2015; r=jrmuizel (1bb2ed20d3) - Bug 1266878 - Fix off-by-one error in ParamTraits<StereoMode> - r=nical (f467d496b8) - Bug 1258758 - rename GetEffectiveClipRect to GetLocalClipRect. r=kats (897798948f) - Bug 1249936 - add a LayerComposite::GetShadowTransform method & simplify Layer::GetLocalTransform. r=botond (fe492a1327) - Bug 1269032 - Add support for user multipliers to pan gesture inputs. r=mstange (ea7698dca2) - Bug 1265510 - Add some scroll-snapping logging to APZC. r=botond (6f2369163a) - Bug 1268523 - Avoid leaking tasks when they are posted to a destroyed APZC instance. r=botond (70e1e63b7b) - Bug 1265510 - Ensure that new input blocks still allow APZCs with interrupted animations to scroll-snap. r=botond (01341b554b) - Bug 1269068 - Zero out the axis velocity once the wheel scroll animation is terminating. r=botond (9a9f384485) - Bug 1246056 - Ensure that the MockContentController's timestamp is always >= GetStartupTime(). r=botond (4fe53e1f24) - Bug 1256341 - Add a gtest to catch scenarios where a tap timeout is interrupted by a non-touch block. r=botond (21a15f5a5a) - Bug 1266833 - When the scroll position is clamped during a frame reconstruction, send a scroll offset update to APZ. r=tnikkel (c0acd70a3d) - Bug 1268517 - Remove unused function. r=botond (534e4d9bae) - Bug 1213095 - Fix APZEventState build with logging enabled. r=botond (ac7c1e9401) - Bug 1250954 - Correct the SharedSurfaceTextureClient when forwarder changed. r=jgilbert (0a389bc0fa) - Bug 1265873 - Use gfxSharedReadLock in TextureClientPool r=nical (e3ed936a8a) - Bug 1265468 - Update the compositor pointer on all TextureSources in the list when recycling in ImageHost. r=nical (78f29244bb) - Bug 1252237 - Remove the TextureHost shmem mapping failed assert. r=milan (d0b7230dff) - Bug 1265112 - When rendering blend modes with CompositorOGL, use NV_texture_barrier (if available) to sample directly from the framebuffer. r=jrmuizel (6f3ae237a5) - Bug 1268878 - Include various of gfxFontEntry's sub-objects in memory reporting. r=njn (b960415fa8) - Bug 1268951 - Reallocate sanitized user font data into an appropriately-sized block, to reduce ongoing RAM footprint. r=jrmuizel (a26e9dd632) - Bug 1123416 - Part 1: Make topsrcdir a valid Gradle project root. r=sebastian (77b0b35fc1) - Bug 1260672: Remove 'nsAutoPtr.h' and clean up includes of Gonk diskspace watcher, r=gsvelto (99c4c35e3d) - Bug 1167535 - Fix fanotify 4g size limit. r=fabrice (c62015e454) - Bug 1167817 - Switch back to printf_stderr in GonkDiskSpaceWatcher.cpp r=dhylands (110445f628) - Bug 1260672: Use |UniquePtr| for Gonk alarms, r=gsvelto (03678b9015) - Bug 1260672: Use |UniquePtr| for Gonk sensors, r=gsvelto (545ec3fc70) - Bug 1263845. When a parent changes from auto height to non-auto height or vice versa, a percentage height non-block child needs to realize it's doing a vertical resize. r=dbaron (05db492bc8) - Bug 1268009 - If APZ is force-disabled, disable paint skipping even for apz-originated scrolls. r=kats (c02f8755cd) - Bug 1267555 part 1 - [css-grid] Make grid-aligned abs.pos. descendants that span from 'auto' to the first/last line stretch to the outer edge of the first/last track respectively. r=dholbert (88f6261043) - Bug 1267555 part 2 - [css-grid] Adjust reftests for abs.pos. "auto/first-line" and "last-line/auto" change. (43d03ab15b)
1455 lines
49 KiB
JavaScript
1455 lines
49 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* 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/. */
|
|
|
|
/* This content script should work in any browser or iframe and should not
|
|
* depend on the frame being contained in tabbrowser. */
|
|
|
|
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
Cu.import("resource:///modules/ContentWebRTC.jsm");
|
|
Cu.import("resource://gre/modules/InlineSpellChecker.jsm");
|
|
Cu.import("resource://gre/modules/InlineSpellCheckerContent.jsm");
|
|
Cu.import("resource://gre/modules/Task.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
|
|
"resource:///modules/E10SUtils.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
|
|
"resource://gre/modules/BrowserUtils.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "ContentLinkHandler",
|
|
"resource:///modules/ContentLinkHandler.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
|
|
"resource://gre/modules/LoginManagerContent.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "InsecurePasswordUtils",
|
|
"resource://gre/modules/InsecurePasswordUtils.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "PluginContent",
|
|
"resource:///modules/PluginContent.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
|
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "FormSubmitObserver",
|
|
"resource:///modules/FormSubmitObserver.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata",
|
|
"resource://gre/modules/PageMetadata.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils",
|
|
"resource:///modules/PlacesUIUtils.jsm");
|
|
XPCOMUtils.defineLazyGetter(this, "PageMenuChild", function() {
|
|
let tmp = {};
|
|
Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
|
|
return new tmp.PageMenuChild();
|
|
});
|
|
XPCOMUtils.defineLazyModuleGetter(this, "Feeds",
|
|
"resource:///modules/Feeds.jsm");
|
|
|
|
// TabChildGlobal
|
|
var global = this;
|
|
|
|
// Load the form validation popup handler
|
|
var formSubmitObserver = new FormSubmitObserver(content, this);
|
|
|
|
addMessageListener("ContextMenu:DoCustomCommand", function(message) {
|
|
E10SUtils.wrapHandlingUserInput(
|
|
content, message.data.handlingUserInput,
|
|
() => PageMenuChild.executeMenu(message.data.generatedItemId));
|
|
});
|
|
|
|
addMessageListener("RemoteLogins:fillForm", function(message) {
|
|
LoginManagerContent.receiveMessage(message, content);
|
|
});
|
|
addEventListener("DOMFormHasPassword", function(event) {
|
|
LoginManagerContent.onDOMFormHasPassword(event, content);
|
|
InsecurePasswordUtils.checkForInsecurePasswords(event.target);
|
|
});
|
|
addEventListener("DOMInputPasswordAdded", function(event) {
|
|
LoginManagerContent.onDOMInputPasswordAdded(event, content);
|
|
});
|
|
addEventListener("pageshow", function(event) {
|
|
LoginManagerContent.onPageShow(event, content);
|
|
});
|
|
addEventListener("DOMAutoComplete", function(event) {
|
|
LoginManagerContent.onUsernameInput(event);
|
|
});
|
|
addEventListener("blur", function(event) {
|
|
LoginManagerContent.onUsernameInput(event);
|
|
});
|
|
|
|
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
|
addEventListener("contextmenu", function (event) {
|
|
let defaultPrevented = event.defaultPrevented;
|
|
if (!Services.prefs.getBoolPref("dom.event.contextmenu.enabled")) {
|
|
let plugin = null;
|
|
try {
|
|
plugin = event.target.QueryInterface(Ci.nsIObjectLoadingContent);
|
|
} catch (e) {}
|
|
if (plugin && plugin.displayedType == Ci.nsIObjectLoadingContent.TYPE_PLUGIN) {
|
|
// Don't open a context menu for plugins.
|
|
return;
|
|
}
|
|
|
|
defaultPrevented = false;
|
|
}
|
|
|
|
if (!defaultPrevented) {
|
|
let editFlags = SpellCheckHelper.isEditable(event.target, content);
|
|
let spellInfo;
|
|
if (editFlags &
|
|
(SpellCheckHelper.EDITABLE | SpellCheckHelper.CONTENTEDITABLE)) {
|
|
spellInfo =
|
|
InlineSpellCheckerContent.initContextMenu(event, editFlags, this);
|
|
}
|
|
|
|
sendSyncMessage("contextmenu", { editFlags, spellInfo }, { event });
|
|
}
|
|
}, false);
|
|
} else {
|
|
}
|
|
|
|
var handleContentContextMenu = function (event) {
|
|
let defaultPrevented = event.defaultPrevented;
|
|
if (!Services.prefs.getBoolPref("dom.event.contextmenu.enabled")) {
|
|
let plugin = null;
|
|
try {
|
|
plugin = event.target.QueryInterface(Ci.nsIObjectLoadingContent);
|
|
} catch (e) {}
|
|
if (plugin && plugin.displayedType == Ci.nsIObjectLoadingContent.TYPE_PLUGIN) {
|
|
// Don't open a context menu for plugins.
|
|
return;
|
|
}
|
|
|
|
defaultPrevented = false;
|
|
}
|
|
|
|
if (defaultPrevented)
|
|
return;
|
|
|
|
let addonInfo = {};
|
|
let subject = {
|
|
event: event,
|
|
addonInfo: addonInfo,
|
|
};
|
|
subject.wrappedJSObject = subject;
|
|
Services.obs.notifyObservers(subject, "content-contextmenu", null);
|
|
|
|
let doc = event.target.ownerDocument;
|
|
let docLocation = doc.mozDocumentURIIfNotForErrorPages;
|
|
docLocation = docLocation && docLocation.spec;
|
|
let charSet = doc.characterSet;
|
|
let baseURI = doc.baseURI;
|
|
let referrer = doc.referrer;
|
|
let referrerPolicy = doc.referrerPolicy;
|
|
let frameOuterWindowID = doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDOMWindowUtils)
|
|
.outerWindowID;
|
|
let loginFillInfo = LoginManagerContent.getFieldContext(event.target);
|
|
|
|
// The same-origin check will be done in nsContextMenu.openLinkInTab.
|
|
let parentAllowsMixedContent = !!docShell.mixedContentChannel;
|
|
|
|
// get referrer attribute from clicked link and parse it
|
|
// if per element referrer is enabled, the element referrer overrules
|
|
// the document wide referrer
|
|
if (Services.prefs.getBoolPref("network.http.enablePerElementReferrer")) {
|
|
let referrerAttrValue = Services.netUtils.parseAttributePolicyString(event.target.
|
|
getAttribute("referrerpolicy"));
|
|
if (referrerAttrValue !== Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
|
|
referrerPolicy = referrerAttrValue;
|
|
}
|
|
}
|
|
|
|
let disableSetDesktopBg = null;
|
|
// Media related cache info parent needs for saving
|
|
let contentType = null;
|
|
let contentDisposition = null;
|
|
if (event.target.nodeType == Ci.nsIDOMNode.ELEMENT_NODE &&
|
|
event.target instanceof Ci.nsIImageLoadingContent &&
|
|
event.target.currentURI) {
|
|
disableSetDesktopBg = disableSetDesktopBackground(event.target);
|
|
|
|
try {
|
|
let imageCache =
|
|
Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
|
.getImgCacheForDocument(doc);
|
|
let props =
|
|
imageCache.findEntryProperties(event.target.currentURI, doc);
|
|
try {
|
|
contentType = props.get("type", Ci.nsISupportsCString).data;
|
|
} catch(e) {}
|
|
try {
|
|
contentDisposition =
|
|
props.get("content-disposition", Ci.nsISupportsCString).data;
|
|
} catch(e) {}
|
|
} catch(e) {}
|
|
}
|
|
|
|
let selectionInfo = BrowserUtils.getSelectionDetails(content);
|
|
|
|
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
|
let editFlags = SpellCheckHelper.isEditable(event.target, content);
|
|
let spellInfo;
|
|
if (editFlags &
|
|
(SpellCheckHelper.EDITABLE | SpellCheckHelper.CONTENTEDITABLE)) {
|
|
spellInfo =
|
|
InlineSpellCheckerContent.initContextMenu(event, editFlags, this);
|
|
}
|
|
|
|
// Set the event target first as the copy image command needs it to
|
|
// determine what was context-clicked on. Then, update the state of the
|
|
// commands on the context menu.
|
|
docShell.contentViewer.QueryInterface(Ci.nsIContentViewerEdit)
|
|
.setCommandNode(event.target);
|
|
event.target.ownerDocument.defaultView.updateCommands("contentcontextmenu");
|
|
|
|
let customMenuItems = PageMenuChild.build(event.target);
|
|
let principal = doc.nodePrincipal;
|
|
sendRpcMessage("contextmenu",
|
|
{ editFlags, spellInfo, customMenuItems, addonInfo,
|
|
principal, docLocation, charSet, baseURI, referrer,
|
|
referrerPolicy, contentType, contentDisposition,
|
|
frameOuterWindowID, selectionInfo, disableSetDesktopBg,
|
|
loginFillInfo, parentAllowsMixedContent },
|
|
{ event, popupNode: event.target });
|
|
}
|
|
else {
|
|
// Break out to the parent window and pass the add-on info along
|
|
let browser = docShell.chromeEventHandler;
|
|
let mainWin = browser.ownerDocument.defaultView;
|
|
mainWin.gContextMenuContentData = {
|
|
isRemote: false,
|
|
event: event,
|
|
popupNode: event.target,
|
|
browser: browser,
|
|
addonInfo: addonInfo,
|
|
documentURIObject: doc.documentURIObject,
|
|
docLocation: docLocation,
|
|
charSet: charSet,
|
|
referrer: referrer,
|
|
referrerPolicy: referrerPolicy,
|
|
contentType: contentType,
|
|
contentDisposition: contentDisposition,
|
|
selectionInfo: selectionInfo,
|
|
disableSetDesktopBackground: disableSetDesktopBg,
|
|
loginFillInfo,
|
|
parentAllowsMixedContent,
|
|
};
|
|
}
|
|
}
|
|
|
|
Cc["@mozilla.org/eventlistenerservice;1"]
|
|
.getService(Ci.nsIEventListenerService)
|
|
.addSystemEventListener(global, "contextmenu", handleContentContextMenu, false);
|
|
|
|
// Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
|
|
const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0;
|
|
const TLS_ERROR_REPORT_TELEMETRY_EXPANDED = 1;
|
|
const TLS_ERROR_REPORT_TELEMETRY_SUCCESS = 6;
|
|
const TLS_ERROR_REPORT_TELEMETRY_FAILURE = 7;
|
|
|
|
var AboutCertErrorListener = {
|
|
init(chromeGlobal) {
|
|
addMessageListener("AboutCertErrorDetails", this);
|
|
chromeGlobal.addEventListener("AboutCertErrorLoad", this, false, true);
|
|
chromeGlobal.addEventListener("AboutCertErrorSetAutomatic", this, false, true);
|
|
},
|
|
|
|
get isAboutCertError() {
|
|
return content.document.documentURI.startsWith("about:certerror");
|
|
},
|
|
|
|
handleEvent(event) {
|
|
if (!this.isAboutCertError) {
|
|
return;
|
|
}
|
|
|
|
switch (event.type) {
|
|
case "AboutCertErrorLoad":
|
|
this.onLoad(event);
|
|
break;
|
|
case "AboutCertErrorSetAutomatic":
|
|
this.onSetAutomatic(event);
|
|
break;
|
|
}
|
|
},
|
|
|
|
receiveMessage(msg) {
|
|
if (!this.isAboutCertError) {
|
|
return;
|
|
}
|
|
|
|
switch (msg.name) {
|
|
case "AboutCertErrorDetails":
|
|
this.onDetails(msg);
|
|
break;
|
|
}
|
|
},
|
|
|
|
onLoad(event) {
|
|
let originalTarget = event.originalTarget;
|
|
let ownerDoc = originalTarget.ownerDocument;
|
|
ClickEventHandler.onAboutCertError(originalTarget, ownerDoc);
|
|
|
|
// Set up the TLS Error Reporting UI - reports are sent automatically
|
|
// (from nsHttpChannel::OnStopRequest) if the user has previously enabled
|
|
// automatic sending of reports. The UI ensures that a report is sent
|
|
// for the certificate error currently displayed if the user enables it
|
|
// here.
|
|
let automatic = Services.prefs.getBoolPref("security.ssl.errorReporting.automatic");
|
|
content.dispatchEvent(new content.CustomEvent("AboutCertErrorOptions", {
|
|
detail: JSON.stringify({
|
|
enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"),
|
|
automatic,
|
|
})
|
|
}));
|
|
},
|
|
|
|
onDetails(msg) {
|
|
let div = content.document.getElementById("certificateErrorText");
|
|
div.textContent = msg.data.info;
|
|
},
|
|
|
|
onSetAutomatic(event) {
|
|
sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
|
|
automatic: event.detail
|
|
});
|
|
|
|
// if we're enabling reports, send a report for this failure
|
|
if (event.detail) {
|
|
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
|
|
.getService(Ci.nsISerializationHelper);
|
|
|
|
let serializable = docShell.failedChannel.securityInfo
|
|
.QueryInterface(Ci.nsITransportSecurityInfo)
|
|
.QueryInterface(Ci.nsISerializable);
|
|
|
|
let serializedSecurityInfo = serhelper.serializeToString(serializable);
|
|
|
|
let {host, port} = content.document.mozDocumentURIIfNotForErrorPages;
|
|
sendAsyncMessage("Browser:SendSSLErrorReport", {
|
|
uri: { host, port },
|
|
securityInfo: serializedSecurityInfo
|
|
});
|
|
}
|
|
},
|
|
};
|
|
|
|
AboutCertErrorListener.init(this);
|
|
|
|
|
|
var AboutNetErrorListener = {
|
|
init: function(chromeGlobal) {
|
|
chromeGlobal.addEventListener('AboutNetErrorLoad', this, false, true);
|
|
chromeGlobal.addEventListener('AboutNetErrorSetAutomatic', this, false, true);
|
|
chromeGlobal.addEventListener('AboutNetErrorOverride', this, false, true);
|
|
},
|
|
|
|
get isAboutNetError() {
|
|
return content.document.documentURI.startsWith("about:neterror");
|
|
},
|
|
|
|
handleEvent: function(aEvent) {
|
|
if (!this.isAboutNetError) {
|
|
return;
|
|
}
|
|
|
|
switch (aEvent.type) {
|
|
case "AboutNetErrorLoad":
|
|
this.onPageLoad(aEvent);
|
|
break;
|
|
case "AboutNetErrorSetAutomatic":
|
|
this.onSetAutomatic(aEvent);
|
|
break;
|
|
case "AboutNetErrorOverride":
|
|
this.onOverride(aEvent);
|
|
break;
|
|
}
|
|
},
|
|
|
|
onPageLoad: function(evt) {
|
|
let automatic = Services.prefs.getBoolPref("security.ssl.errorReporting.automatic");
|
|
content.dispatchEvent(new content.CustomEvent("AboutNetErrorOptions", {
|
|
detail: JSON.stringify({
|
|
enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"),
|
|
automatic: automatic
|
|
})
|
|
}));
|
|
|
|
sendAsyncMessage("Browser:SSLErrorReportTelemetry",
|
|
{reportStatus: TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN});
|
|
},
|
|
|
|
onSetAutomatic: function(evt) {
|
|
sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
|
|
automatic: evt.detail
|
|
});
|
|
|
|
// if we're enabling reports, send a report for this failure
|
|
if (evt.detail) {
|
|
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
|
|
.getService(Ci.nsISerializationHelper);
|
|
|
|
let serializable = docShell.failedChannel.securityInfo
|
|
.QueryInterface(Ci.nsITransportSecurityInfo)
|
|
.QueryInterface(Ci.nsISerializable);
|
|
|
|
let serializedSecurityInfo = serhelper.serializeToString(serializable);
|
|
|
|
let {host, port} = content.document.mozDocumentURIIfNotForErrorPages;
|
|
sendAsyncMessage("Browser:SendSSLErrorReport", {
|
|
uri: { host, port },
|
|
securityInfo: serializedSecurityInfo
|
|
});
|
|
|
|
}
|
|
},
|
|
|
|
onOverride: function(evt) {
|
|
let {host, port} = content.document.mozDocumentURIIfNotForErrorPages;
|
|
sendAsyncMessage("Browser:OverrideWeakCrypto", { uri: {host, port} });
|
|
}
|
|
}
|
|
|
|
AboutNetErrorListener.init(this);
|
|
|
|
|
|
var ClickEventHandler = {
|
|
init: function init() {
|
|
Cc["@mozilla.org/eventlistenerservice;1"]
|
|
.getService(Ci.nsIEventListenerService)
|
|
.addSystemEventListener(global, "click", this, true);
|
|
},
|
|
|
|
handleEvent: function(event) {
|
|
if (!event.isTrusted || event.defaultPrevented || event.button == 2) {
|
|
return;
|
|
}
|
|
|
|
let originalTarget = event.originalTarget;
|
|
let ownerDoc = originalTarget.ownerDocument;
|
|
if (!ownerDoc) {
|
|
return;
|
|
}
|
|
|
|
// Handle click events from about pages
|
|
if (ownerDoc.documentURI.startsWith("about:certerror")) {
|
|
this.onAboutCertError(originalTarget, ownerDoc);
|
|
return;
|
|
} else if (ownerDoc.documentURI.startsWith("about:blocked")) {
|
|
this.onAboutBlocked(originalTarget, ownerDoc);
|
|
return;
|
|
} else if (ownerDoc.documentURI.startsWith("about:neterror")) {
|
|
this.onAboutNetError(event, ownerDoc.documentURI);
|
|
return;
|
|
}
|
|
|
|
let [href, node, principal] = this._hrefAndLinkNodeForClickEvent(event);
|
|
|
|
// get referrer attribute from clicked link and parse it
|
|
// if per element referrer is enabled, the element referrer overrules
|
|
// the document wide referrer
|
|
let referrerPolicy = ownerDoc.referrerPolicy;
|
|
if (Services.prefs.getBoolPref("network.http.enablePerElementReferrer") &&
|
|
node) {
|
|
let referrerAttrValue = Services.netUtils.parseAttributePolicyString(node.
|
|
getAttribute("referrerpolicy"));
|
|
if (referrerAttrValue !== Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
|
|
referrerPolicy = referrerAttrValue;
|
|
}
|
|
}
|
|
|
|
let json = { button: event.button, shiftKey: event.shiftKey,
|
|
ctrlKey: event.ctrlKey, metaKey: event.metaKey,
|
|
altKey: event.altKey, href: null, title: null,
|
|
bookmark: false, referrerPolicy: referrerPolicy };
|
|
|
|
if (href) {
|
|
try {
|
|
BrowserUtils.urlSecurityCheck(href, principal);
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
|
|
json.href = href;
|
|
if (node) {
|
|
json.title = node.getAttribute("title");
|
|
if (event.button == 0 && !event.ctrlKey && !event.shiftKey &&
|
|
!event.altKey && !event.metaKey) {
|
|
json.bookmark = node.getAttribute("rel") == "sidebar";
|
|
if (json.bookmark) {
|
|
event.preventDefault(); // Need to prevent the pageload.
|
|
}
|
|
}
|
|
}
|
|
json.noReferrer = BrowserUtils.linkHasNoReferrer(node)
|
|
|
|
// Check if the link needs to be opened with mixed content allowed.
|
|
// Only when the owner doc has |mixedContentChannel| and the same origin
|
|
// should we allow mixed content.
|
|
json.allowMixedContent = false;
|
|
let docshell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
.QueryInterface(Ci.nsIDocShell);
|
|
if (docShell.mixedContentChannel) {
|
|
const sm = Services.scriptSecurityManager;
|
|
try {
|
|
let targetURI = BrowserUtils.makeURI(href);
|
|
sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false);
|
|
json.allowMixedContent = true;
|
|
} catch (e) {}
|
|
}
|
|
|
|
sendAsyncMessage("Content:Click", json);
|
|
return;
|
|
}
|
|
|
|
// This might be middle mouse navigation.
|
|
if (event.button == 1) {
|
|
sendAsyncMessage("Content:Click", json);
|
|
}
|
|
},
|
|
|
|
onAboutCertError: function (targetElement, ownerDoc) {
|
|
let docshell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
.QueryInterface(Ci.nsIDocShell);
|
|
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
|
|
.getService(Ci.nsISerializationHelper);
|
|
let serializedSecurityInfo = "";
|
|
|
|
try {
|
|
let serializable = docShell.failedChannel.securityInfo
|
|
.QueryInterface(Ci.nsITransportSecurityInfo)
|
|
.QueryInterface(Ci.nsISerializable);
|
|
|
|
serializedSecurityInfo = serhelper.serializeToString(serializable);
|
|
} catch (e) { }
|
|
|
|
sendAsyncMessage("Browser:CertExceptionError", {
|
|
location: ownerDoc.location.href,
|
|
elementId: targetElement.getAttribute("id"),
|
|
isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
|
|
securityInfoAsString: serializedSecurityInfo
|
|
});
|
|
},
|
|
|
|
onAboutBlocked: function (targetElement, ownerDoc) {
|
|
var reason = 'phishing';
|
|
if (/e=malwareBlocked/.test(ownerDoc.documentURI)) {
|
|
reason = 'malware';
|
|
} else if (/e=unwantedBlocked/.test(ownerDoc.documentURI)) {
|
|
reason = 'unwanted';
|
|
} else if (/e=forbiddenBlocked/.test(ownerDoc.documentURI)) {
|
|
reason = 'forbidden';
|
|
}
|
|
sendAsyncMessage("Browser:SiteBlockedError", {
|
|
location: ownerDoc.location.href,
|
|
reason: reason,
|
|
elementId: targetElement.getAttribute("id"),
|
|
isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView)
|
|
});
|
|
},
|
|
|
|
onAboutNetError: function (event, documentURI) {
|
|
let elmId = event.originalTarget.getAttribute("id");
|
|
if (elmId == "returnButton") {
|
|
sendAsyncMessage("Browser:SSLErrorGoBack", {});
|
|
return;
|
|
}
|
|
if (elmId != "errorTryAgain" || !/e=netOffline/.test(documentURI)) {
|
|
return;
|
|
}
|
|
// browser front end will handle clearing offline mode and refreshing
|
|
// the page *if* we're in offline mode now. Otherwise let the error page
|
|
// handle the click.
|
|
if (Services.io.offline) {
|
|
event.preventDefault();
|
|
sendAsyncMessage("Browser:EnableOnlineMode", {});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Extracts linkNode and href for the current click target.
|
|
*
|
|
* @param event
|
|
* The click event.
|
|
* @return [href, linkNode, linkPrincipal].
|
|
*
|
|
* @note linkNode will be null if the click wasn't on an anchor
|
|
* element. This includes SVG links, because callers expect |node|
|
|
* to behave like an <a> element, which SVG links (XLink) don't.
|
|
*/
|
|
_hrefAndLinkNodeForClickEvent: function(event) {
|
|
function isHTMLLink(aNode) {
|
|
// Be consistent with what nsContextMenu.js does.
|
|
return ((aNode instanceof content.HTMLAnchorElement && aNode.href) ||
|
|
(aNode instanceof content.HTMLAreaElement && aNode.href) ||
|
|
aNode instanceof content.HTMLLinkElement);
|
|
}
|
|
|
|
let node = event.target;
|
|
while (node && !isHTMLLink(node)) {
|
|
node = node.parentNode;
|
|
}
|
|
|
|
if (node)
|
|
return [node.href, node, node.ownerDocument.nodePrincipal];
|
|
|
|
// If there is no linkNode, try simple XLink.
|
|
let href, baseURI;
|
|
node = event.target;
|
|
while (node && !href) {
|
|
if (node.nodeType == content.Node.ELEMENT_NODE &&
|
|
(node.localName == "a" ||
|
|
node.namespaceURI == "http://www.w3.org/1998/Math/MathML")) {
|
|
href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
|
|
if (href) {
|
|
baseURI = node.ownerDocument.baseURIObject;
|
|
break;
|
|
}
|
|
}
|
|
node = node.parentNode;
|
|
}
|
|
|
|
// In case of XLink, we don't return the node we got href from since
|
|
// callers expect <a>-like elements.
|
|
// Note: makeURI() will throw if aUri is not a valid URI.
|
|
return [href ? BrowserUtils.makeURI(href, null, baseURI).spec : null, null,
|
|
node && node.ownerDocument.nodePrincipal];
|
|
}
|
|
};
|
|
ClickEventHandler.init();
|
|
|
|
ContentLinkHandler.init(this);
|
|
|
|
// TODO: Load this lazily so the JSM is run only if a relevant event/message fires.
|
|
var pluginContent = new PluginContent(global);
|
|
|
|
addEventListener("DOMWebNotificationClicked", function(event) {
|
|
sendAsyncMessage("DOMWebNotificationClicked", {});
|
|
}, false);
|
|
|
|
addEventListener("DOMServiceWorkerFocusClient", function(event) {
|
|
sendAsyncMessage("DOMServiceWorkerFocusClient", {});
|
|
}, false);
|
|
|
|
ContentWebRTC.init();
|
|
addMessageListener("rtcpeer:Allow", ContentWebRTC);
|
|
addMessageListener("rtcpeer:Deny", ContentWebRTC);
|
|
addMessageListener("webrtc:Allow", ContentWebRTC);
|
|
addMessageListener("webrtc:Deny", ContentWebRTC);
|
|
addMessageListener("webrtc:StopSharing", ContentWebRTC);
|
|
addMessageListener("webrtc:StartBrowserSharing", () => {
|
|
let windowID = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
|
|
sendAsyncMessage("webrtc:response:StartBrowserSharing", {
|
|
windowID: windowID
|
|
});
|
|
});
|
|
|
|
addEventListener("pageshow", function(event) {
|
|
if (event.target == content.document) {
|
|
sendAsyncMessage("PageVisibility:Show", {
|
|
persisted: event.persisted,
|
|
});
|
|
}
|
|
});
|
|
|
|
var PageMetadataMessenger = {
|
|
init() {
|
|
addMessageListener("PageMetadata:GetPageData", this);
|
|
addMessageListener("PageMetadata:GetMicrodata", this);
|
|
},
|
|
receiveMessage(message) {
|
|
switch(message.name) {
|
|
case "PageMetadata:GetPageData": {
|
|
let result = PageMetadata.getData(content.document);
|
|
sendAsyncMessage("PageMetadata:PageDataResult", result);
|
|
break;
|
|
}
|
|
|
|
case "PageMetadata:GetMicrodata": {
|
|
let target = message.objects;
|
|
let result = PageMetadata.getMicrodata(content.document, target);
|
|
sendAsyncMessage("PageMetadata:MicrodataResult", result);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PageMetadataMessenger.init();
|
|
|
|
addMessageListener("ContextMenu:SaveVideoFrameAsImage", (message) => {
|
|
let video = message.objects.target;
|
|
let canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
|
canvas.width = video.videoWidth;
|
|
canvas.height = video.videoHeight;
|
|
|
|
let ctxDraw = canvas.getContext("2d");
|
|
ctxDraw.drawImage(video, 0, 0);
|
|
sendAsyncMessage("ContextMenu:SaveVideoFrameAsImage:Result", {
|
|
dataURL: canvas.toDataURL("image/jpeg", ""),
|
|
});
|
|
});
|
|
|
|
addMessageListener("ContextMenu:MediaCommand", (message) => {
|
|
let media = message.objects.element;
|
|
|
|
switch (message.data.command) {
|
|
case "play":
|
|
media.play();
|
|
break;
|
|
case "pause":
|
|
media.pause();
|
|
break;
|
|
case "mute":
|
|
media.muted = true;
|
|
break;
|
|
case "unmute":
|
|
media.muted = false;
|
|
break;
|
|
case "playbackRate":
|
|
media.playbackRate = message.data.data;
|
|
break;
|
|
case "hidecontrols":
|
|
media.removeAttribute("controls");
|
|
break;
|
|
case "showcontrols":
|
|
media.setAttribute("controls", "true");
|
|
break;
|
|
case "hidestats":
|
|
case "showstats":
|
|
let event = media.ownerDocument.createEvent("CustomEvent");
|
|
event.initCustomEvent("media-showStatistics", false, true,
|
|
message.data.command == "showstats");
|
|
media.dispatchEvent(event);
|
|
break;
|
|
case "fullscreen":
|
|
if (content.document.fullscreenEnabled)
|
|
media.requestFullscreen();
|
|
break;
|
|
}
|
|
});
|
|
|
|
addMessageListener("ContextMenu:Canvas:ToDataURL", (message) => {
|
|
let dataURL = message.objects.target.toDataURL();
|
|
sendAsyncMessage("ContextMenu:Canvas:ToDataURL:Result", { dataURL });
|
|
});
|
|
|
|
addMessageListener("ContextMenu:ReloadFrame", (message) => {
|
|
message.objects.target.ownerDocument.location.reload();
|
|
});
|
|
|
|
addMessageListener("ContextMenu:ReloadImage", (message) => {
|
|
let image = message.objects.target;
|
|
if (image instanceof Ci.nsIImageLoadingContent)
|
|
image.forceReload();
|
|
});
|
|
|
|
addMessageListener("ContextMenu:BookmarkFrame", (message) => {
|
|
let frame = message.objects.target.ownerDocument;
|
|
sendAsyncMessage("ContextMenu:BookmarkFrame:Result",
|
|
{ title: frame.title,
|
|
description: PlacesUIUtils.getDescriptionFromDocument(frame) });
|
|
});
|
|
|
|
addMessageListener("ContextMenu:SearchFieldBookmarkData", (message) => {
|
|
let node = message.objects.target;
|
|
|
|
let charset = node.ownerDocument.characterSet;
|
|
|
|
let formBaseURI = BrowserUtils.makeURI(node.form.baseURI,
|
|
charset);
|
|
|
|
let formURI = BrowserUtils.makeURI(node.form.getAttribute("action"),
|
|
charset,
|
|
formBaseURI);
|
|
|
|
let spec = formURI.spec;
|
|
|
|
let isURLEncoded =
|
|
(node.form.method.toUpperCase() == "POST"
|
|
&& (node.form.enctype == "application/x-www-form-urlencoded" ||
|
|
node.form.enctype == ""));
|
|
|
|
let title = node.ownerDocument.title;
|
|
let description = PlacesUIUtils.getDescriptionFromDocument(node.ownerDocument);
|
|
|
|
let formData = [];
|
|
|
|
function escapeNameValuePair(aName, aValue, aIsFormUrlEncoded) {
|
|
if (aIsFormUrlEncoded)
|
|
return escape(aName + "=" + aValue);
|
|
else
|
|
return escape(aName) + "=" + escape(aValue);
|
|
}
|
|
|
|
for (let el of node.form.elements) {
|
|
if (!el.type) // happens with fieldsets
|
|
continue;
|
|
|
|
if (el == node) {
|
|
formData.push((isURLEncoded) ? escapeNameValuePair(el.name, "%s", true) :
|
|
// Don't escape "%s", just append
|
|
escapeNameValuePair(el.name, "", false) + "%s");
|
|
continue;
|
|
}
|
|
|
|
let type = el.type.toLowerCase();
|
|
|
|
if (((el instanceof content.HTMLInputElement && el.mozIsTextField(true)) ||
|
|
type == "hidden" || type == "textarea") ||
|
|
((type == "checkbox" || type == "radio") && el.checked)) {
|
|
formData.push(escapeNameValuePair(el.name, el.value, isURLEncoded));
|
|
} else if (el instanceof content.HTMLSelectElement && el.selectedIndex >= 0) {
|
|
for (let j=0; j < el.options.length; j++) {
|
|
if (el.options[j].selected)
|
|
formData.push(escapeNameValuePair(el.name, el.options[j].value,
|
|
isURLEncoded));
|
|
}
|
|
}
|
|
}
|
|
|
|
let postData;
|
|
|
|
if (isURLEncoded)
|
|
postData = formData.join("&");
|
|
else {
|
|
let separator = spec.includes("?") ? "&" : "?";
|
|
spec += separator + formData.join("&");
|
|
}
|
|
|
|
sendAsyncMessage("ContextMenu:SearchFieldBookmarkData:Result",
|
|
{ spec, title, description, postData, charset });
|
|
});
|
|
|
|
addMessageListener("Bookmarks:GetPageDetails", (message) => {
|
|
let doc = content.document;
|
|
let isErrorPage = /^about:(neterror|certerror|blocked)/.test(doc.documentURI);
|
|
sendAsyncMessage("Bookmarks:GetPageDetails:Result",
|
|
{ isErrorPage: isErrorPage,
|
|
description: PlacesUIUtils.getDescriptionFromDocument(doc) });
|
|
});
|
|
|
|
|
|
addMessageListener("ContextMenu:BookmarkFrame", (message) => {
|
|
let frame = message.objects.target.ownerDocument;
|
|
sendAsyncMessage("ContextMenu:BookmarkFrame:Result",
|
|
{ title: frame.title,
|
|
description: PlacesUIUtils.getDescriptionFromDocument(frame) });
|
|
});
|
|
|
|
var LightWeightThemeWebInstallListener = {
|
|
_previewWindow: null,
|
|
|
|
init: function() {
|
|
addEventListener("InstallBrowserTheme", this, false, true);
|
|
addEventListener("PreviewBrowserTheme", this, false, true);
|
|
addEventListener("ResetBrowserThemePreview", this, false, true);
|
|
},
|
|
|
|
handleEvent: function (event) {
|
|
switch (event.type) {
|
|
case "InstallBrowserTheme": {
|
|
sendAsyncMessage("LightWeightThemeWebInstaller:Install", {
|
|
baseURI: event.target.baseURI,
|
|
themeData: event.target.getAttribute("data-browsertheme"),
|
|
});
|
|
break;
|
|
}
|
|
case "PreviewBrowserTheme": {
|
|
sendAsyncMessage("LightWeightThemeWebInstaller:Preview", {
|
|
baseURI: event.target.baseURI,
|
|
themeData: event.target.getAttribute("data-browsertheme"),
|
|
});
|
|
this._previewWindow = event.target.ownerDocument.defaultView;
|
|
this._previewWindow.addEventListener("pagehide", this, true);
|
|
break;
|
|
}
|
|
case "pagehide": {
|
|
sendAsyncMessage("LightWeightThemeWebInstaller:ResetPreview");
|
|
this._resetPreviewWindow();
|
|
break;
|
|
}
|
|
case "ResetBrowserThemePreview": {
|
|
if (this._previewWindow) {
|
|
sendAsyncMessage("LightWeightThemeWebInstaller:ResetPreview",
|
|
{baseURI: event.target.baseURI});
|
|
this._resetPreviewWindow();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
|
|
_resetPreviewWindow: function () {
|
|
this._previewWindow.removeEventListener("pagehide", this, true);
|
|
this._previewWindow = null;
|
|
}
|
|
};
|
|
|
|
LightWeightThemeWebInstallListener.init();
|
|
|
|
function disableSetDesktopBackground(aTarget) {
|
|
// Disable the Set as Desktop Background menu item if we're still trying
|
|
// to load the image or the load failed.
|
|
if (!(aTarget instanceof Ci.nsIImageLoadingContent))
|
|
return true;
|
|
|
|
if (("complete" in aTarget) && !aTarget.complete)
|
|
return true;
|
|
|
|
if (aTarget.currentURI.schemeIs("javascript"))
|
|
return true;
|
|
|
|
let request = aTarget.QueryInterface(Ci.nsIImageLoadingContent)
|
|
.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
|
|
if (!request)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
addMessageListener("ContextMenu:SetAsDesktopBackground", (message) => {
|
|
let target = message.objects.target;
|
|
|
|
// Paranoia: check disableSetDesktopBackground again, in case the
|
|
// image changed since the context menu was initiated.
|
|
let disable = disableSetDesktopBackground(target);
|
|
|
|
if (!disable) {
|
|
try {
|
|
BrowserUtils.urlSecurityCheck(target.currentURI.spec, target.ownerDocument.nodePrincipal);
|
|
let canvas = content.document.createElement("canvas");
|
|
canvas.width = target.naturalWidth;
|
|
canvas.height = target.naturalHeight;
|
|
let ctx = canvas.getContext("2d");
|
|
ctx.drawImage(target, 0, 0);
|
|
let dataUrl = canvas.toDataURL();
|
|
sendAsyncMessage("ContextMenu:SetAsDesktopBackground:Result",
|
|
{ dataUrl });
|
|
}
|
|
catch (e) {
|
|
Cu.reportError(e);
|
|
disable = true;
|
|
}
|
|
}
|
|
|
|
if (disable)
|
|
sendAsyncMessage("ContextMenu:SetAsDesktopBackground:Result", { disable });
|
|
});
|
|
|
|
var PageInfoListener = {
|
|
|
|
init: function() {
|
|
addMessageListener("PageInfo:getData", this);
|
|
},
|
|
|
|
receiveMessage: function(message) {
|
|
let strings = message.data.strings;
|
|
let window;
|
|
let document;
|
|
|
|
let frameOuterWindowID = message.data.frameOuterWindowID;
|
|
|
|
// If inside frame then get the frame's window and document.
|
|
if (frameOuterWindowID) {
|
|
window = Services.wm.getOuterWindowWithId(frameOuterWindowID);
|
|
document = window.document;
|
|
}
|
|
else {
|
|
window = content.window;
|
|
document = content.document;
|
|
}
|
|
|
|
let imageElement = message.objects.imageElement;
|
|
|
|
let pageInfoData = {metaViewRows: this.getMetaInfo(document),
|
|
docInfo: this.getDocumentInfo(document),
|
|
feeds: this.getFeedsInfo(document, strings),
|
|
windowInfo: this.getWindowInfo(window),
|
|
imageInfo: this.getImageInfo(imageElement)};
|
|
|
|
sendAsyncMessage("PageInfo:data", pageInfoData);
|
|
|
|
// Separate step so page info dialog isn't blank while waiting for this to finish.
|
|
this.getMediaInfo(document, window, strings);
|
|
},
|
|
|
|
getImageInfo: function(imageElement) {
|
|
let imageInfo = null;
|
|
if (imageElement) {
|
|
imageInfo = {
|
|
currentSrc: imageElement.currentSrc,
|
|
width: imageElement.width,
|
|
height: imageElement.height,
|
|
imageText: imageElement.title || imageElement.alt
|
|
};
|
|
}
|
|
return imageInfo;
|
|
},
|
|
|
|
getMetaInfo: function(document) {
|
|
let metaViewRows = [];
|
|
|
|
// Get the meta tags from the page.
|
|
let metaNodes = document.getElementsByTagName("meta");
|
|
|
|
for (let metaNode of metaNodes) {
|
|
metaViewRows.push([metaNode.name || metaNode.httpEquiv || metaNode.getAttribute("property"),
|
|
metaNode.content]);
|
|
}
|
|
|
|
return metaViewRows;
|
|
},
|
|
|
|
getWindowInfo: function(window) {
|
|
let windowInfo = {};
|
|
windowInfo.isTopWindow = window == window.top;
|
|
|
|
let hostName = null;
|
|
try {
|
|
hostName = window.location.host;
|
|
}
|
|
catch (exception) { }
|
|
|
|
windowInfo.hostName = hostName;
|
|
return windowInfo;
|
|
},
|
|
|
|
getDocumentInfo: function(document) {
|
|
let docInfo = {};
|
|
docInfo.title = document.title;
|
|
docInfo.location = document.location.toString();
|
|
docInfo.referrer = document.referrer;
|
|
docInfo.compatMode = document.compatMode;
|
|
docInfo.contentType = document.contentType;
|
|
docInfo.characterSet = document.characterSet;
|
|
docInfo.lastModified = document.lastModified;
|
|
|
|
let documentURIObject = {};
|
|
documentURIObject.spec = document.documentURIObject.spec;
|
|
documentURIObject.originCharset = document.documentURIObject.originCharset;
|
|
docInfo.documentURIObject = documentURIObject;
|
|
|
|
docInfo.isContentWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(content);
|
|
|
|
return docInfo;
|
|
},
|
|
|
|
getFeedsInfo: function(document, strings) {
|
|
let feeds = [];
|
|
// Get the feeds from the page.
|
|
let linkNodes = document.getElementsByTagName("link");
|
|
let length = linkNodes.length;
|
|
for (let i = 0; i < length; i++) {
|
|
let link = linkNodes[i];
|
|
if (!link.href) {
|
|
continue;
|
|
}
|
|
let rel = link.rel && link.rel.toLowerCase();
|
|
let rels = {};
|
|
|
|
if (rel) {
|
|
for (let relVal of rel.split(/\s+/)) {
|
|
rels[relVal] = true;
|
|
}
|
|
}
|
|
|
|
if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) {
|
|
let type = Feeds.isValidFeed(link, document.nodePrincipal, "feed" in rels);
|
|
if (type) {
|
|
type = strings[type] || strings["application/rss+xml"];
|
|
feeds.push([link.title, type, link.href]);
|
|
}
|
|
}
|
|
}
|
|
return feeds;
|
|
},
|
|
|
|
// Only called once to get the media tab's media elements from the content page.
|
|
getMediaInfo: function(document, window, strings)
|
|
{
|
|
let frameList = this.goThroughFrames(document, window);
|
|
Task.spawn(() => this.processFrames(document, frameList, strings));
|
|
},
|
|
|
|
goThroughFrames: function(document, window)
|
|
{
|
|
let frameList = [document];
|
|
if (window && window.frames.length > 0) {
|
|
let num = window.frames.length;
|
|
for (let i = 0; i < num; i++) {
|
|
// Recurse through the frames.
|
|
frameList.concat(this.goThroughFrames(window.frames[i].document,
|
|
window.frames[i]));
|
|
}
|
|
}
|
|
return frameList;
|
|
},
|
|
|
|
processFrames: function*(document, frameList, strings)
|
|
{
|
|
let nodeCount = 0;
|
|
for (let doc of frameList) {
|
|
let iterator = doc.createTreeWalker(doc, content.NodeFilter.SHOW_ELEMENT);
|
|
|
|
// Goes through all the elements on the doc. imageViewRows takes only the media elements.
|
|
while (iterator.nextNode()) {
|
|
let mediaNode = this.getMediaNode(document, strings, iterator.currentNode);
|
|
|
|
if (mediaNode) {
|
|
sendAsyncMessage("PageInfo:mediaData",
|
|
{imageViewRow: mediaNode, isComplete: false});
|
|
}
|
|
|
|
if (++nodeCount % 500 == 0) {
|
|
// setTimeout every 500 elements so we don't keep blocking the content process.
|
|
yield new Promise(resolve => setTimeout(resolve, 10));
|
|
}
|
|
}
|
|
}
|
|
// Send that page info media fetching has finished.
|
|
sendAsyncMessage("PageInfo:mediaData", {isComplete: true});
|
|
},
|
|
|
|
getMediaNode: function(document, strings, elem)
|
|
{
|
|
// Check for images defined in CSS (e.g. background, borders), any node may have multiple.
|
|
let computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, "");
|
|
let mediaElement = null;
|
|
|
|
let addImage = (url, type, alt, elem, isBg) => {
|
|
let element = this.serializeElementInfo(document, url, type, alt, elem, isBg);
|
|
mediaElement = [url, type, alt, element, isBg];
|
|
};
|
|
|
|
if (computedStyle) {
|
|
let addImgFunc = (label, val) => {
|
|
if (val.primitiveType == content.CSSPrimitiveValue.CSS_URI) {
|
|
addImage(val.getStringValue(), label, strings.notSet, elem, true);
|
|
}
|
|
else if (val.primitiveType == content.CSSPrimitiveValue.CSS_STRING) {
|
|
// This is for -moz-image-rect.
|
|
// TODO: Reimplement once bug 714757 is fixed.
|
|
let strVal = val.getStringValue();
|
|
if (strVal.search(/^.*url\(\"?/) > -1) {
|
|
let url = strVal.replace(/^.*url\(\"?/,"").replace(/\"?\).*$/,"");
|
|
addImage(url, label, strings.notSet, elem, true);
|
|
}
|
|
}
|
|
else if (val.cssValueType == content.CSSValue.CSS_VALUE_LIST) {
|
|
// Recursively resolve multiple nested CSS value lists.
|
|
for (let i = 0; i < val.length; i++) {
|
|
addImgFunc(label, val.item(i));
|
|
}
|
|
}
|
|
};
|
|
|
|
addImgFunc(strings.mediaBGImg, computedStyle.getPropertyCSSValue("background-image"));
|
|
addImgFunc(strings.mediaBorderImg, computedStyle.getPropertyCSSValue("border-image-source"));
|
|
addImgFunc(strings.mediaListImg, computedStyle.getPropertyCSSValue("list-style-image"));
|
|
addImgFunc(strings.mediaCursor, computedStyle.getPropertyCSSValue("cursor"));
|
|
}
|
|
|
|
// One swi^H^H^Hif-else to rule them all.
|
|
if (elem instanceof content.HTMLImageElement) {
|
|
addImage(elem.src, strings.mediaImg,
|
|
(elem.hasAttribute("alt")) ? elem.alt : strings.notSet, elem, false);
|
|
}
|
|
else if (elem instanceof content.SVGImageElement) {
|
|
try {
|
|
// Note: makeURLAbsolute will throw if either the baseURI is not a valid URI
|
|
// or the URI formed from the baseURI and the URL is not a valid URI.
|
|
let href = makeURLAbsolute(elem.baseURI, elem.href.baseVal);
|
|
addImage(href, strings.mediaImg, "", elem, false);
|
|
} catch (e) { }
|
|
}
|
|
else if (elem instanceof content.HTMLVideoElement) {
|
|
addImage(elem.currentSrc, strings.mediaVideo, "", elem, false);
|
|
}
|
|
else if (elem instanceof content.HTMLAudioElement) {
|
|
addImage(elem.currentSrc, strings.mediaAudio, "", elem, false);
|
|
}
|
|
else if (elem instanceof content.HTMLLinkElement) {
|
|
if (elem.rel && /\bicon\b/i.test(elem.rel)) {
|
|
addImage(elem.href, strings.mediaLink, "", elem, false);
|
|
}
|
|
}
|
|
else if (elem instanceof content.HTMLInputElement || elem instanceof content.HTMLButtonElement) {
|
|
if (elem.type.toLowerCase() == "image") {
|
|
addImage(elem.src, strings.mediaInput,
|
|
(elem.hasAttribute("alt")) ? elem.alt : strings.notSet, elem, false);
|
|
}
|
|
}
|
|
else if (elem instanceof content.HTMLObjectElement) {
|
|
addImage(elem.data, strings.mediaObject, this.getValueText(elem), elem, false);
|
|
}
|
|
else if (elem instanceof content.HTMLEmbedElement) {
|
|
addImage(elem.src, strings.mediaEmbed, "", elem, false);
|
|
}
|
|
|
|
return mediaElement;
|
|
},
|
|
|
|
/**
|
|
* Set up a JSON element object with all the instanceOf and other infomation that
|
|
* makePreview in pageInfo.js uses to figure out how to display the preview.
|
|
*/
|
|
|
|
serializeElementInfo: function(document, url, type, alt, item, isBG)
|
|
{
|
|
let result = {};
|
|
|
|
let imageText;
|
|
if (!isBG &&
|
|
!(item instanceof content.SVGImageElement) &&
|
|
!(document instanceof content.ImageDocument)) {
|
|
imageText = item.title || item.alt;
|
|
|
|
if (!imageText && !(item instanceof content.HTMLImageElement)) {
|
|
imageText = this.getValueText(item);
|
|
}
|
|
}
|
|
|
|
result.imageText = imageText;
|
|
result.longDesc = item.longDesc;
|
|
result.numFrames = 1;
|
|
|
|
if (item instanceof content.HTMLObjectElement ||
|
|
item instanceof content.HTMLEmbedElement ||
|
|
item instanceof content.HTMLLinkElement) {
|
|
result.mimeType = item.type;
|
|
}
|
|
|
|
if (!result.mimeType && !isBG && item instanceof Ci.nsIImageLoadingContent) {
|
|
// Interface for image loading content.
|
|
let imageRequest = item.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
|
|
if (imageRequest) {
|
|
result.mimeType = imageRequest.mimeType;
|
|
let image = !(imageRequest.imageStatus & imageRequest.STATUS_ERROR) && imageRequest.image;
|
|
if (image) {
|
|
result.numFrames = image.numFrames;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we have a data url, get the MIME type from the url.
|
|
if (!result.mimeType && url.startsWith("data:")) {
|
|
let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url);
|
|
if (dataMimeType)
|
|
result.mimeType = dataMimeType[1].toLowerCase();
|
|
}
|
|
|
|
result.HTMLLinkElement = item instanceof content.HTMLLinkElement;
|
|
result.HTMLInputElement = item instanceof content.HTMLInputElement;
|
|
result.HTMLImageElement = item instanceof content.HTMLImageElement;
|
|
result.HTMLObjectElement = item instanceof content.HTMLObjectElement;
|
|
result.SVGImageElement = item instanceof content.SVGImageElement;
|
|
result.HTMLVideoElement = item instanceof content.HTMLVideoElement;
|
|
result.HTMLAudioElement = item instanceof content.HTMLAudioElement;
|
|
|
|
if (isBG) {
|
|
// Items that are showing this image as a background
|
|
// image might not necessarily have a width or height,
|
|
// so we'll dynamically generate an image and send up the
|
|
// natural dimensions.
|
|
let img = content.document.createElement("img");
|
|
img.src = url;
|
|
result.naturalWidth = img.naturalWidth;
|
|
result.naturalHeight = img.naturalHeight;
|
|
} else {
|
|
// Otherwise, we can use the current width and height
|
|
// of the image.
|
|
result.width = item.width;
|
|
result.height = item.height;
|
|
}
|
|
|
|
if (item instanceof content.SVGImageElement) {
|
|
result.SVGImageElementWidth = item.width.baseVal.value;
|
|
result.SVGImageElementHeight = item.height.baseVal.value;
|
|
}
|
|
|
|
result.baseURI = item.baseURI;
|
|
|
|
return result;
|
|
},
|
|
|
|
//******** Other Misc Stuff
|
|
// Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
|
|
// parse a node to extract the contents of the node
|
|
getValueText: function(node)
|
|
{
|
|
|
|
let valueText = "";
|
|
|
|
// Form input elements don't generally contain information that is useful to our callers, so return nothing.
|
|
if (node instanceof content.HTMLInputElement ||
|
|
node instanceof content.HTMLSelectElement ||
|
|
node instanceof content.HTMLTextAreaElement) {
|
|
return valueText;
|
|
}
|
|
|
|
// Otherwise recurse for each child.
|
|
let length = node.childNodes.length;
|
|
|
|
for (let i = 0; i < length; i++) {
|
|
let childNode = node.childNodes[i];
|
|
let nodeType = childNode.nodeType;
|
|
|
|
// Text nodes are where the goods are.
|
|
if (nodeType == content.Node.TEXT_NODE) {
|
|
valueText += " " + childNode.nodeValue;
|
|
}
|
|
// And elements can have more text inside them.
|
|
else if (nodeType == content.Node.ELEMENT_NODE) {
|
|
// Images are special, we want to capture the alt text as if the image weren't there.
|
|
if (childNode instanceof content.HTMLImageElement) {
|
|
valueText += " " + this.getAltText(childNode);
|
|
}
|
|
else {
|
|
valueText += " " + this.getValueText(childNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
return this.stripWS(valueText);
|
|
},
|
|
|
|
// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
|
|
// Traverse the tree in search of an img or area element and grab its alt tag.
|
|
getAltText: function(node)
|
|
{
|
|
let altText = "";
|
|
|
|
if (node.alt) {
|
|
return node.alt;
|
|
}
|
|
let length = node.childNodes.length;
|
|
for (let i = 0; i < length; i++) {
|
|
if ((altText = this.getAltText(node.childNodes[i]) != undefined)) { // stupid js warning...
|
|
return altText;
|
|
}
|
|
}
|
|
return "";
|
|
},
|
|
|
|
// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
|
|
// Strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space.
|
|
stripWS: function(text)
|
|
{
|
|
let middleRE = /\s+/g;
|
|
let endRE = /(^\s+)|(\s+$)/g;
|
|
|
|
text = text.replace(middleRE, " ");
|
|
return text.replace(endRE, "");
|
|
}
|
|
};
|
|
PageInfoListener.init();
|
|
|
|
let OfflineApps = {
|
|
_docId: 0,
|
|
_docIdMap: new Map(),
|
|
|
|
_docManifestSet: new Set(),
|
|
|
|
_observerAdded: false,
|
|
registerWindow(aWindow) {
|
|
if (!this._observerAdded) {
|
|
this._observerAdded = true;
|
|
Services.obs.addObserver(this, "offline-cache-update-completed", true);
|
|
}
|
|
let manifestURI = this._getManifestURI(aWindow);
|
|
this._docManifestSet.add(manifestURI.spec);
|
|
},
|
|
|
|
handleEvent(event) {
|
|
if (event.type == "MozApplicationManifest") {
|
|
this.offlineAppRequested(event.originalTarget.defaultView);
|
|
}
|
|
},
|
|
|
|
_getManifestURI(aWindow) {
|
|
if (!aWindow.document.documentElement)
|
|
return null;
|
|
|
|
var attr = aWindow.document.documentElement.getAttribute("manifest");
|
|
if (!attr)
|
|
return null;
|
|
|
|
try {
|
|
var contentURI = BrowserUtils.makeURI(aWindow.location.href, null, null);
|
|
return BrowserUtils.makeURI(attr, aWindow.document.characterSet, contentURI);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
},
|
|
|
|
offlineAppRequested(aContentWindow) {
|
|
this.registerWindow(aContentWindow);
|
|
if (!Services.prefs.getBoolPref("browser.offline-apps.notify")) {
|
|
return;
|
|
}
|
|
|
|
let currentURI = aContentWindow.document.documentURIObject;
|
|
// don't bother showing UI if the user has already made a decision
|
|
if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
|
|
return;
|
|
|
|
try {
|
|
if (Services.prefs.getBoolPref("offline-apps.allow_by_default")) {
|
|
// all pages can use offline capabilities, no need to ask the user
|
|
return;
|
|
}
|
|
} catch(e) {
|
|
// this pref isn't set by default, ignore failures
|
|
}
|
|
let docId = ++this._docId;
|
|
this._docIdMap.set(docId, Cu.getWeakReference(aContentWindow.document));
|
|
sendAsyncMessage("OfflineApps:RequestPermission", {
|
|
uri: currentURI.spec,
|
|
docId,
|
|
});
|
|
},
|
|
|
|
_startFetching(aDocument) {
|
|
if (!aDocument.documentElement)
|
|
return;
|
|
|
|
let manifestURI = this._getManifestURI(aDocument.defaultView);
|
|
if (!manifestURI)
|
|
return;
|
|
|
|
var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
|
|
getService(Ci.nsIOfflineCacheUpdateService);
|
|
updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject,
|
|
aDocument.nodePrincipal, aDocument.defaultView);
|
|
},
|
|
|
|
receiveMessage(aMessage) {
|
|
if (aMessage.name == "OfflineApps:StartFetching") {
|
|
let doc = this._docIdMap.get(aMessage.data.docId);
|
|
doc = doc && doc.get();
|
|
if (doc) {
|
|
this._startFetching(doc);
|
|
}
|
|
this._docIdMap.delete(aMessage.data.docId);
|
|
}
|
|
},
|
|
|
|
observe(aSubject, aTopic, aState) {
|
|
if (aTopic == "offline-cache-update-completed") {
|
|
let cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
|
|
let uri = cacheUpdate.manifestURI;
|
|
if (uri && this._docManifestSet.has(uri.spec)) {
|
|
sendAsyncMessage("OfflineApps:CheckUsage", {uri: uri.spec});
|
|
}
|
|
}
|
|
},
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
|
Ci.nsISupportsWeakReference]),
|
|
};
|
|
|
|
addEventListener("MozApplicationManifest", OfflineApps, false);
|
|
addMessageListener("OfflineApps:StartFetching", OfflineApps);
|
|
|