2010-06-07 19:44:06 -07:00
|
|
|
// This stays here because otherwise it's hard to tell if there's a parsing error
|
2010-06-08 08:22:20 -07:00
|
|
|
dump("###################################### content loaded\n");
|
2010-06-07 19:44:06 -07:00
|
|
|
|
|
|
|
let Cc = Components.classes;
|
|
|
|
let Ci = Components.interfaces;
|
2010-07-13 07:36:09 -07:00
|
|
|
let Cu = Components.utils;
|
|
|
|
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
|
2010-06-07 19:44:06 -07:00
|
|
|
let gFocusManager = Cc["@mozilla.org/focus-manager;1"]
|
|
|
|
.getService(Ci.nsIFocusManager);
|
|
|
|
|
|
|
|
let XULDocument = Ci.nsIDOMXULDocument;
|
|
|
|
let HTMLHtmlElement = Ci.nsIDOMHTMLHtmlElement;
|
|
|
|
let HTMLIFrameElement = Ci.nsIDOMHTMLIFrameElement;
|
|
|
|
let HTMLFrameElement = Ci.nsIDOMHTMLFrameElement;
|
|
|
|
|
2010-09-03 02:01:25 -07:00
|
|
|
// Blindly copied from Safari documentation for now.
|
|
|
|
const kViewportMinScale = 0;
|
|
|
|
const kViewportMaxScale = 10;
|
|
|
|
const kViewportMinWidth = 200;
|
|
|
|
const kViewportMaxWidth = 10000;
|
|
|
|
const kViewportMinHeight = 223;
|
|
|
|
const kViewportMaxHeight = 10000;
|
2010-06-07 19:44:06 -07:00
|
|
|
|
2010-11-18 11:36:46 -08:00
|
|
|
const kReferenceDpi = 240; // standard "pixel" size used in some preferences
|
|
|
|
|
2010-06-07 19:44:06 -07:00
|
|
|
/** Watches for mouse click in content and redirect them to the best found target **/
|
|
|
|
const ElementTouchHelper = {
|
|
|
|
get radius() {
|
2010-07-13 07:36:09 -07:00
|
|
|
let prefs = Services.prefs;
|
2010-06-07 19:44:06 -07:00
|
|
|
delete this.radius;
|
2010-07-13 07:36:09 -07:00
|
|
|
return this.radius = { "top": prefs.getIntPref("browser.ui.touch.top"),
|
|
|
|
"right": prefs.getIntPref("browser.ui.touch.right"),
|
|
|
|
"bottom": prefs.getIntPref("browser.ui.touch.bottom"),
|
|
|
|
"left": prefs.getIntPref("browser.ui.touch.left")
|
2010-06-07 19:44:06 -07:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
get weight() {
|
|
|
|
delete this.weight;
|
2010-07-13 07:36:09 -07:00
|
|
|
return this.weight = { "visited": Services.prefs.getIntPref("browser.ui.touch.weight.visited")
|
2010-06-07 19:44:06 -07:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
/* Retrieve the closest element to a point by looking at borders position */
|
|
|
|
getClosest: function getClosest(aWindowUtils, aX, aY) {
|
2010-11-18 11:36:46 -08:00
|
|
|
let dpiRatio = aWindowUtils.displayDPI / kReferenceDpi;
|
|
|
|
|
2010-06-07 19:44:06 -07:00
|
|
|
let target = aWindowUtils.elementFromPoint(aX, aY,
|
|
|
|
true, /* ignore root scroll frame*/
|
|
|
|
false); /* don't flush layout */
|
|
|
|
|
2010-09-20 12:13:00 -07:00
|
|
|
// return early if the click is just over a clickable element
|
|
|
|
if (this._isElementClickable(target))
|
|
|
|
return target;
|
|
|
|
|
2010-11-18 11:36:46 -08:00
|
|
|
let nodes = aWindowUtils.nodesFromRect(aX, aY, this.radius.top * dpiRatio,
|
|
|
|
this.radius.right * dpiRatio,
|
|
|
|
this.radius.bottom * dpiRatio,
|
|
|
|
this.radius.left * dpiRatio, true, false);
|
2010-06-07 19:44:06 -07:00
|
|
|
|
|
|
|
let threshold = Number.POSITIVE_INFINITY;
|
|
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
|
|
let current = nodes[i];
|
|
|
|
if (!current.mozMatchesSelector || !this._isElementClickable(current))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
let rect = current.getBoundingClientRect();
|
|
|
|
let distance = this._computeDistanceFromRect(aX, aY, rect);
|
|
|
|
|
|
|
|
// increase a little bit the weight for already visited items
|
|
|
|
if (current && current.mozMatchesSelector("*:visited"))
|
|
|
|
distance *= (this.weight.visited / 100);
|
|
|
|
|
|
|
|
if (distance < threshold) {
|
|
|
|
target = current;
|
|
|
|
threshold = distance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return target;
|
|
|
|
},
|
|
|
|
|
2010-09-20 12:13:00 -07:00
|
|
|
_isElementClickable: function _isElementClickable(aElement) {
|
|
|
|
const selector = "a,:link,:visited,[role=button],button,input,select,textarea,label";
|
|
|
|
for (let elem = aElement; elem; elem = elem.parentNode) {
|
|
|
|
if (this._hasMouseListener(elem))
|
|
|
|
return true;
|
|
|
|
if (elem.mozMatchesSelector && elem.mozMatchesSelector(selector))
|
|
|
|
return true;
|
2010-06-07 19:44:06 -07:00
|
|
|
}
|
2010-09-20 12:13:00 -07:00
|
|
|
return false;
|
2010-06-07 19:44:06 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
_computeDistanceFromRect: function _computeDistanceFromRect(aX, aY, aRect) {
|
|
|
|
let x = 0, y = 0;
|
|
|
|
let xmost = aRect.left + aRect.width;
|
|
|
|
let ymost = aRect.top + aRect.height;
|
|
|
|
|
|
|
|
// compute horizontal distance from left/right border depending if X is
|
|
|
|
// before/inside/after the element's rectangle
|
|
|
|
if (aRect.left < aX && aX < xmost)
|
|
|
|
x = Math.min(xmost - aX, aX - aRect.left);
|
|
|
|
else if (aX < aRect.left)
|
|
|
|
x = aRect.left - aX;
|
|
|
|
else if (aX > xmost)
|
|
|
|
x = aX - xmost;
|
|
|
|
|
|
|
|
// compute vertical distance from top/bottom border depending if Y is
|
|
|
|
// above/inside/below the element's rectangle
|
|
|
|
if (aRect.top < aY && aY < ymost)
|
|
|
|
y = Math.min(ymost - aY, aY - aRect.top);
|
|
|
|
else if (aY < aRect.top)
|
|
|
|
y = aRect.top - aY;
|
|
|
|
if (aY > ymost)
|
|
|
|
y = aY - ymost;
|
|
|
|
|
|
|
|
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
|
|
|
|
},
|
|
|
|
|
|
|
|
_els: Cc["@mozilla.org/eventlistenerservice;1"].getService(Ci.nsIEventListenerService),
|
|
|
|
_clickableEvents: ["mousedown", "mouseup", "click"],
|
|
|
|
_hasMouseListener: function _hasMouseListener(aElement) {
|
|
|
|
let els = this._els;
|
|
|
|
let listeners = els.getListenerInfoFor(aElement, {});
|
|
|
|
for (let i = 0; i < listeners.length; i++) {
|
|
|
|
if (this._clickableEvents.indexOf(listeners[i].type) != -1)
|
|
|
|
return true;
|
|
|
|
}
|
2010-06-14 09:59:08 -07:00
|
|
|
return false;
|
2010-06-07 19:44:06 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-06-21 13:16:37 -07:00
|
|
|
|
2010-06-07 19:44:06 -07:00
|
|
|
/**
|
|
|
|
* @param x,y Browser coordinates
|
|
|
|
* @return Element at position, null if no active browser or no element found
|
|
|
|
*/
|
|
|
|
function elementFromPoint(x, y) {
|
|
|
|
// browser's elementFromPoint expect browser-relative client coordinates.
|
|
|
|
// subtract browser's scroll values to adjust
|
|
|
|
let cwu = Util.getWindowUtils(content);
|
|
|
|
let scroll = Util.getScrollOffset(content);
|
2010-07-20 13:49:31 -07:00
|
|
|
x = x - scroll.x;
|
2010-06-07 19:44:06 -07:00
|
|
|
y = y - scroll.y;
|
|
|
|
let elem = ElementTouchHelper.getClosest(cwu, x, y);
|
|
|
|
|
|
|
|
// step through layers of IFRAMEs and FRAMES to find innermost element
|
|
|
|
while (elem && (elem instanceof HTMLIFrameElement || elem instanceof HTMLFrameElement)) {
|
|
|
|
// adjust client coordinates' origin to be top left of iframe viewport
|
|
|
|
let rect = elem.getBoundingClientRect();
|
2010-06-21 13:16:37 -07:00
|
|
|
x -= rect.left;
|
|
|
|
y -= rect.top;
|
2010-06-07 19:44:06 -07:00
|
|
|
let windowUtils = elem.contentDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
|
|
|
elem = ElementTouchHelper.getClosest(windowUtils, x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
return elem;
|
|
|
|
}
|
|
|
|
|
2010-06-21 13:16:37 -07:00
|
|
|
function getBoundingContentRect(aElement) {
|
|
|
|
if (!aElement)
|
2010-06-07 19:44:06 -07:00
|
|
|
return new Rect(0, 0, 0, 0);
|
|
|
|
|
2010-06-21 13:16:37 -07:00
|
|
|
let document = aElement.ownerDocument;
|
2010-06-07 19:44:06 -07:00
|
|
|
while(document.defaultView.frameElement)
|
|
|
|
document = document.defaultView.frameElement.ownerDocument;
|
|
|
|
|
|
|
|
let offset = Util.getScrollOffset(content);
|
2010-06-21 13:16:37 -07:00
|
|
|
let r = aElement.getBoundingClientRect();
|
2010-06-07 19:44:06 -07:00
|
|
|
|
|
|
|
// step out of iframes and frames, offsetting scroll values
|
2010-06-21 13:16:37 -07:00
|
|
|
for (let frame = aElement.ownerDocument.defaultView; frame != content; frame = frame.parent) {
|
2010-06-07 19:44:06 -07:00
|
|
|
// adjust client coordinates' origin to be top left of iframe viewport
|
|
|
|
let rect = frame.frameElement.getBoundingClientRect();
|
|
|
|
let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth;
|
|
|
|
let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth;
|
|
|
|
offset.add(rect.left + parseInt(left), rect.top + parseInt(top));
|
|
|
|
}
|
|
|
|
|
|
|
|
return new Rect(r.left + offset.x, r.top + offset.y, r.width, r.height);
|
|
|
|
}
|
|
|
|
|
2010-06-21 13:16:37 -07:00
|
|
|
function getContentClientRects(aElement) {
|
|
|
|
let offset = Util.getScrollOffset(content);
|
|
|
|
let nativeRects = aElement.getClientRects();
|
|
|
|
// step out of iframes and frames, offsetting scroll values
|
|
|
|
for (let frame = aElement.ownerDocument.defaultView; frame != content; frame = frame.parent) {
|
|
|
|
// adjust client coordinates' origin to be top left of iframe viewport
|
|
|
|
let rect = frame.frameElement.getBoundingClientRect();
|
|
|
|
let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth;
|
|
|
|
let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth;
|
|
|
|
offset.add(rect.left + parseInt(left), rect.top + parseInt(top));
|
|
|
|
}
|
|
|
|
|
|
|
|
let result = [];
|
|
|
|
for (let i = nativeRects.length - 1; i >= 0; i--) {
|
|
|
|
let r = nativeRects[i];
|
|
|
|
result.push({ left: r.left + offset.x,
|
|
|
|
top: r.top + offset.y,
|
|
|
|
width: r.width,
|
|
|
|
height: r.height
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
};
|
2010-06-07 19:44:06 -07:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Responsible for sending messages about security, location, and page load state.
|
|
|
|
* @param loadingController Object with methods startLoading and stopLoading
|
|
|
|
*/
|
|
|
|
function ProgressController(loadingController) {
|
|
|
|
this._webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
|
|
|
|
this._overrideService = null;
|
|
|
|
this._hostChanged = false;
|
|
|
|
this._state = null;
|
|
|
|
this._loadingController = loadingController || this._defaultLoadingController;
|
|
|
|
}
|
|
|
|
|
|
|
|
ProgressController.prototype = {
|
|
|
|
// Default loading callbacks do nothing
|
|
|
|
_defaultLoadingController: {
|
|
|
|
startLoading: function() {},
|
|
|
|
stopLoading: function() {}
|
|
|
|
},
|
|
|
|
|
|
|
|
onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
|
|
|
|
// ignore notification that aren't about the main document (iframes, etc)
|
2010-06-21 13:04:59 -07:00
|
|
|
let win = aWebProgress.DOMWindow;
|
2010-06-07 19:44:06 -07:00
|
|
|
if (win != win.parent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If you want to observe other state flags, be sure they're listed in the
|
|
|
|
// Tab._createBrowser's call to addProgressListener
|
|
|
|
if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
|
|
|
|
if (aStateFlags & Ci.nsIWebProgressListener.STATE_START) {
|
|
|
|
this._loadingController.startLoading();
|
|
|
|
}
|
|
|
|
else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
|
|
|
|
this._loadingController.stopLoading();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/** This method is called to indicate progress changes for the currently loading page. */
|
|
|
|
onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
|
|
|
|
},
|
|
|
|
|
|
|
|
/** This method is called to indicate a change to the current location. */
|
|
|
|
onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI) {
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This method is called to indicate a status changes for the currently
|
|
|
|
* loading page. The message is already formatted for display.
|
|
|
|
*/
|
|
|
|
onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
|
|
|
|
},
|
|
|
|
|
|
|
|
/** This method is called when the security state of the browser changes. */
|
|
|
|
onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
|
|
|
|
},
|
|
|
|
|
|
|
|
QueryInterface: function QueryInterface(aIID) {
|
|
|
|
if (aIID.equals(Ci.nsIWebProgressListener) ||
|
|
|
|
aIID.equals(Ci.nsISupportsWeakReference) ||
|
|
|
|
aIID.equals(Ci.nsISupports)) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw Components.results.NS_ERROR_NO_INTERFACE;
|
|
|
|
},
|
|
|
|
|
|
|
|
start: function start() {
|
|
|
|
let flags = Ci.nsIWebProgress.NOTIFY_STATE_NETWORK;
|
|
|
|
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIWebProgress);
|
|
|
|
webProgress.addProgressListener(this, flags);
|
|
|
|
},
|
|
|
|
|
|
|
|
stop: function stop() {
|
|
|
|
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIWebProgress);
|
|
|
|
webProgress.removeProgressListener(this);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** Can't think of a good description of this class. It probably does too much? */
|
|
|
|
function Content() {
|
2010-11-29 10:15:03 -08:00
|
|
|
addMessageListener("Browser:Blur", this);
|
2010-09-27 08:11:56 -07:00
|
|
|
addMessageListener("Browser:KeyEvent", this);
|
2010-06-21 13:16:37 -07:00
|
|
|
addMessageListener("Browser:MouseDown", this);
|
|
|
|
addMessageListener("Browser:MouseUp", this);
|
2010-06-09 19:12:05 -07:00
|
|
|
addMessageListener("Browser:SaveAs", this);
|
2010-06-24 15:17:35 -07:00
|
|
|
addMessageListener("Browser:ZoomToPoint", this);
|
2010-10-19 12:32:00 -07:00
|
|
|
addMessageListener("Browser:MozApplicationCache:Fetch", this);
|
2010-06-07 19:44:06 -07:00
|
|
|
|
2010-09-15 23:28:12 -07:00
|
|
|
if (Util.isParentProcess())
|
|
|
|
addEventListener("DOMActivate", this, true);
|
|
|
|
|
|
|
|
addEventListener("MozApplicationManifest", this, false);
|
2010-10-28 08:58:37 -07:00
|
|
|
addEventListener("command", this, false);
|
2010-08-19 15:10:39 -07:00
|
|
|
|
2010-06-07 19:44:06 -07:00
|
|
|
this._progressController = new ProgressController(this);
|
|
|
|
this._progressController.start();
|
|
|
|
|
2010-06-21 13:36:56 -07:00
|
|
|
this._formAssistant = new FormAssistant();
|
2010-06-07 19:44:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Content.prototype = {
|
2010-09-15 23:28:12 -07:00
|
|
|
handleEvent: function handleEvent(aEvent) {
|
|
|
|
switch (aEvent.type) {
|
|
|
|
case "DOMActivate": {
|
|
|
|
// In a local tab, open remote links in new tabs.
|
2010-11-22 12:08:49 -08:00
|
|
|
let target = aEvent.originalTarget;
|
|
|
|
let href = Util.getHrefForElement(target);
|
2010-09-16 09:42:05 -07:00
|
|
|
if (/^http(s?):/.test(href)) {
|
2010-09-15 23:28:12 -07:00
|
|
|
aEvent.preventDefault();
|
2010-11-22 12:08:49 -08:00
|
|
|
sendAsyncMessage("Browser:OpenURI", { uri: href,
|
|
|
|
referrer: target.ownerDocument.documentURIObject.spec,
|
|
|
|
bringFront: true });
|
2010-09-15 23:28:12 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case "MozApplicationManifest": {
|
|
|
|
let doc = aEvent.originalTarget;
|
|
|
|
sendAsyncMessage("Browser:MozApplicationManifest", {
|
|
|
|
location: doc.documentURIObject.spec,
|
|
|
|
manifest: doc.documentElement.getAttribute("manifest"),
|
|
|
|
charset: doc.characterSet
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
2010-10-28 08:58:37 -07:00
|
|
|
case "command": {
|
|
|
|
// Don't trust synthetic events
|
|
|
|
if (!aEvent.isTrusted)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let ot = aEvent.originalTarget;
|
|
|
|
let errorDoc = ot.ownerDocument;
|
|
|
|
|
|
|
|
// If the event came from an ssl error page, it is probably either the "Add
|
|
|
|
// Exception…" or "Get me out of here!" button
|
|
|
|
if (/^about:certerror\?e=nssBadCert/.test(errorDoc.documentURI)) {
|
|
|
|
let perm = errorDoc.getElementById("permanentExceptionButton");
|
|
|
|
let temp = errorDoc.getElementById("temporaryExceptionButton");
|
|
|
|
if (ot == temp || ot == perm) {
|
|
|
|
let action = (ot == perm ? "permanent" : "temporary");
|
|
|
|
sendAsyncMessage("Browser:CertException", { url: errorDoc.location.href, action: action });
|
|
|
|
}
|
|
|
|
else if (ot == errorDoc.getElementById("getMeOutOfHereButton")) {
|
|
|
|
sendAsyncMessage("Browser:CertException", { url: errorDoc.location.href, action: "leave" });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (/^about:neterror\?e=netOffline/.test(errorDoc.documentURI)) {
|
|
|
|
if (ot == errorDoc.getElementById("errorTryAgain")) {
|
|
|
|
// Make sure we're online before attempting to load
|
|
|
|
Util.forceOnline();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2010-09-15 23:28:12 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-06-09 19:12:05 -07:00
|
|
|
receiveMessage: function receiveMessage(aMessage) {
|
|
|
|
let json = aMessage.json;
|
2010-06-21 13:16:37 -07:00
|
|
|
let x = json.x;
|
|
|
|
let y = json.y;
|
2010-06-29 11:15:07 -07:00
|
|
|
let modifiers = json.modifiers;
|
2010-06-07 19:44:06 -07:00
|
|
|
|
2010-06-09 19:12:05 -07:00
|
|
|
switch (aMessage.name) {
|
2010-11-29 10:15:03 -08:00
|
|
|
case "Browser:Blur":
|
|
|
|
gFocusManager.clearFocus(content);
|
|
|
|
break;
|
2010-09-27 08:11:56 -07:00
|
|
|
case "Browser:KeyEvent":
|
|
|
|
let utils = Util.getWindowUtils(content);
|
2010-11-02 10:08:43 -07:00
|
|
|
let defaultAction;
|
|
|
|
if (!Util.isParentProcess())
|
|
|
|
defaultAction = utils.sendKeyEvent(json.type, json.keyCode, json.charCode, modifiers);
|
2010-09-27 08:11:56 -07:00
|
|
|
if (defaultAction && json.type == "keypress") {
|
|
|
|
const masks = Ci.nsIDOMNSEvent;
|
|
|
|
sendAsyncMessage("Browser:KeyPress", {
|
|
|
|
ctrlKey: json.modifiers & masks.CONTROL_MASK,
|
|
|
|
shiftKey: json.modifiers & masks.SHIFT_MASK,
|
|
|
|
metaKey: json.modifiers & masks.META_MASK,
|
|
|
|
keyCode: json.keyCode,
|
|
|
|
charCode: json.charCode
|
|
|
|
});
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2010-09-22 15:12:41 -07:00
|
|
|
case "Browser:MouseDown": {
|
2010-06-29 11:15:07 -07:00
|
|
|
let element = elementFromPoint(x, y);
|
|
|
|
if (!element)
|
|
|
|
return;
|
|
|
|
|
2010-09-16 13:24:00 -07:00
|
|
|
let highlightRects = null;
|
|
|
|
if (element.mozMatchesSelector("*:link,*:visited,*[role=button],button,input,option,select,textarea,label"))
|
|
|
|
highlightRects = getContentClientRects(element);
|
|
|
|
else if (element.mozMatchesSelector("*:link *, *:visited *"))
|
|
|
|
highlightRects = getContentClientRects(element.parentNode);
|
|
|
|
|
|
|
|
if (highlightRects)
|
|
|
|
sendAsyncMessage("Browser:Highlight", { rects: highlightRects, messageId: json.messageId });
|
2010-07-09 01:15:23 -07:00
|
|
|
|
2010-09-22 16:27:11 -07:00
|
|
|
ContextHandler.messageId = json.messageId;
|
|
|
|
|
2010-09-22 15:12:41 -07:00
|
|
|
let event = content.document.createEvent("PopupEvents");
|
|
|
|
event.initEvent("contextmenu", true, true);
|
|
|
|
element.dispatchEvent(event);
|
2010-06-21 13:16:37 -07:00
|
|
|
break;
|
2010-09-22 15:12:41 -07:00
|
|
|
}
|
2010-06-09 19:12:05 -07:00
|
|
|
|
2010-06-24 15:17:35 -07:00
|
|
|
case "Browser:MouseUp": {
|
2010-11-08 02:53:42 -08:00
|
|
|
this._formAssistant.focusSync = true;
|
2010-06-09 19:12:05 -07:00
|
|
|
let element = elementFromPoint(x, y);
|
2010-06-29 11:15:07 -07:00
|
|
|
if (modifiers == Ci.nsIDOMNSEvent.CONTROL_MASK) {
|
|
|
|
let uri = Util.getHrefForElement(element);
|
|
|
|
if (uri)
|
2010-11-22 12:08:49 -08:00
|
|
|
sendAsyncMessage("Browser:OpenURI", { uri: uri,
|
2010-12-01 11:59:02 -08:00
|
|
|
referrer: element.ownerDocument.documentURIObject.spec });
|
2010-06-29 11:15:07 -07:00
|
|
|
} else if (!this._formAssistant.open(element)) {
|
2010-07-21 16:35:47 -07:00
|
|
|
sendAsyncMessage("FindAssist:Hide", { });
|
2010-09-15 00:54:45 -07:00
|
|
|
this._sendMouseEvent("mousemove", element, x, y);
|
2010-07-09 01:15:23 -07:00
|
|
|
this._sendMouseEvent("mousedown", element, x, y);
|
2010-06-09 19:12:05 -07:00
|
|
|
this._sendMouseEvent("mouseup", element, x, y);
|
2010-06-21 13:36:56 -07:00
|
|
|
}
|
2010-10-12 08:57:03 -07:00
|
|
|
ContextHandler.reset();
|
2010-11-08 02:53:42 -08:00
|
|
|
this._formAssistant.focusSync = false;
|
2010-07-01 12:39:31 -07:00
|
|
|
break;
|
2010-06-24 15:17:35 -07:00
|
|
|
}
|
2010-06-09 19:12:05 -07:00
|
|
|
|
|
|
|
case "Browser:SaveAs":
|
|
|
|
if (json.type != Ci.nsIPrintSettings.kOutputFormatPDF)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let printSettings = Cc["@mozilla.org/gfx/printsettings-service;1"]
|
|
|
|
.getService(Ci.nsIPrintSettingsService)
|
|
|
|
.newPrintSettings;
|
|
|
|
printSettings.printSilent = true;
|
|
|
|
printSettings.showPrintProgress = false;
|
|
|
|
printSettings.printBGImages = true;
|
|
|
|
printSettings.printBGColors = true;
|
|
|
|
printSettings.printToFile = true;
|
|
|
|
printSettings.toFileName = json.filePath;
|
|
|
|
printSettings.printFrameType = Ci.nsIPrintSettings.kFramesAsIs;
|
|
|
|
printSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
|
|
|
|
|
|
|
|
//XXX we probably need a preference here, the header can be useful
|
|
|
|
printSettings.footerStrCenter = "";
|
|
|
|
printSettings.footerStrLeft = "";
|
|
|
|
printSettings.footerStrRight = "";
|
|
|
|
printSettings.headerStrCenter = "";
|
|
|
|
printSettings.headerStrLeft = "";
|
|
|
|
printSettings.headerStrRight = "";
|
|
|
|
|
|
|
|
let listener = {
|
|
|
|
onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
|
|
|
|
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
|
|
|
|
sendAsyncMessage("Browser:SaveAs:Return", { type: json.type, id: json.id, referrer: json.referrer });
|
|
|
|
}
|
|
|
|
},
|
|
|
|
onProgressChange : function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {},
|
|
|
|
|
|
|
|
// stubs for the nsIWebProgressListener interfaces which nsIWebBrowserPrint doesn't use.
|
|
|
|
onLocationChange : function() { throw "Unexpected onLocationChange"; },
|
|
|
|
onStatusChange : function() { throw "Unexpected onStatusChange"; },
|
|
|
|
onSecurityChange : function() { throw "Unexpected onSecurityChange"; }
|
|
|
|
};
|
|
|
|
|
|
|
|
let webBrowserPrint = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebBrowserPrint);
|
|
|
|
webBrowserPrint.print(printSettings, listener);
|
|
|
|
break;
|
2010-06-24 15:17:35 -07:00
|
|
|
|
|
|
|
case "Browser:ZoomToPoint": {
|
|
|
|
let rect = null;
|
|
|
|
let element = elementFromPoint(x, y);
|
2010-07-22 20:55:58 -07:00
|
|
|
let win = element.ownerDocument.defaultView;
|
|
|
|
while (element && win.getComputedStyle(element,null).display == "inline")
|
|
|
|
element = element.parentNode;
|
2010-06-24 15:17:35 -07:00
|
|
|
if (element)
|
|
|
|
rect = getBoundingContentRect(element);
|
|
|
|
sendAsyncMessage("Browser:ZoomToPoint:Return", { x: x, y: y, rect: rect });
|
|
|
|
break;
|
|
|
|
}
|
2010-10-19 12:32:00 -07:00
|
|
|
|
|
|
|
case "Browser:MozApplicationCache:Fetch": {
|
|
|
|
let currentURI = Services.io.newURI(json.location, json.charset, null);
|
|
|
|
let manifestURI = Services.io.newURI(json.manifest, json.charset, currentURI);
|
|
|
|
let updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"]
|
|
|
|
.getService(Ci.nsIOfflineCacheUpdateService);
|
|
|
|
updateService.scheduleUpdate(manifestURI, currentURI, content);
|
|
|
|
break;
|
|
|
|
}
|
2010-06-09 19:12:05 -07:00
|
|
|
}
|
2010-06-07 19:44:06 -07:00
|
|
|
},
|
|
|
|
|
2010-06-22 13:33:57 -07:00
|
|
|
_sendMouseEvent: function _sendMouseEvent(aName, aElement, aX, aY) {
|
|
|
|
// the element can be out of the aX/aY point because of the touch radius
|
|
|
|
if (!(aElement instanceof HTMLHtmlElement)) {
|
|
|
|
let isTouchClick = true;
|
|
|
|
let rects = getContentClientRects(aElement);
|
|
|
|
for (let i = 0; i < rects.length; i++) {
|
|
|
|
let rect = rects[i];
|
|
|
|
if ((aX > rect.left && aX < (rect.left + rect.width)) &&
|
|
|
|
(aY > rect.top && aY < (rect.top + rect.height))) {
|
|
|
|
isTouchClick = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-06-07 19:44:06 -07:00
|
|
|
|
2010-06-22 13:33:57 -07:00
|
|
|
if (isTouchClick) {
|
|
|
|
let rect = rects[0];
|
|
|
|
let point = (new Rect(rect.left, rect.top, rect.width, rect.height)).center();
|
|
|
|
aX = point.x;
|
|
|
|
aY = point.y;
|
|
|
|
}
|
2010-06-07 19:44:06 -07:00
|
|
|
}
|
|
|
|
|
2010-06-29 11:34:48 -07:00
|
|
|
let scrollOffset = Util.getScrollOffset(content);
|
2010-06-22 13:33:57 -07:00
|
|
|
let windowUtils = Util.getWindowUtils(content);
|
2010-08-27 15:11:00 -07:00
|
|
|
windowUtils.sendMouseEventToWindow(aName, aX - scrollOffset.x, aY - scrollOffset.y, 0, 1, 0, true);
|
2010-06-07 19:44:06 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
startLoading: function startLoading() {
|
|
|
|
this._loading = true;
|
|
|
|
},
|
|
|
|
|
|
|
|
stopLoading: function stopLoading() {
|
|
|
|
this._loading = false;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
let contentObject = new Content();
|
2010-06-09 16:07:12 -07:00
|
|
|
|
|
|
|
let ViewportHandler = {
|
|
|
|
init: function init() {
|
2010-06-10 18:35:16 -07:00
|
|
|
addEventListener("DOMWindowCreated", this, false);
|
2010-06-09 16:07:12 -07:00
|
|
|
addEventListener("DOMMetaAdded", this, false);
|
2010-06-10 18:35:16 -07:00
|
|
|
addEventListener("DOMContentLoaded", this, false);
|
2010-06-09 16:07:12 -07:00
|
|
|
addEventListener("pageshow", this, false);
|
|
|
|
},
|
|
|
|
|
|
|
|
handleEvent: function handleEvent(aEvent) {
|
2010-09-24 17:41:10 -07:00
|
|
|
let target = aEvent.originalTarget;
|
|
|
|
let isRootDocument = (target == content.document || target.ownerDocument == content.document);
|
|
|
|
if (!isRootDocument)
|
|
|
|
return;
|
|
|
|
|
2010-06-09 16:07:12 -07:00
|
|
|
switch (aEvent.type) {
|
2010-06-10 18:35:16 -07:00
|
|
|
case "DOMWindowCreated":
|
|
|
|
this.resetMetadata();
|
|
|
|
break;
|
|
|
|
|
2010-06-09 16:07:12 -07:00
|
|
|
case "DOMMetaAdded":
|
2010-09-24 17:41:10 -07:00
|
|
|
if (target.name == "viewport")
|
2010-06-09 16:07:12 -07:00
|
|
|
this.updateMetadata();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "DOMContentLoaded":
|
|
|
|
case "pageshow":
|
2010-09-24 17:41:10 -07:00
|
|
|
this.updateMetadata();
|
2010-06-09 16:07:12 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-06-10 18:35:16 -07:00
|
|
|
resetMetadata: function resetMetadata() {
|
2010-09-17 10:59:09 -07:00
|
|
|
sendAsyncMessage("Browser:ViewportMetadata", null);
|
2010-06-09 16:07:12 -07:00
|
|
|
},
|
|
|
|
|
2010-06-10 18:35:16 -07:00
|
|
|
updateMetadata: function updateMetadata() {
|
2010-11-16 14:50:34 -08:00
|
|
|
sendAsyncMessage("Browser:ViewportMetadata", this.getViewportMetadata());
|
2010-06-09 16:07:12 -07:00
|
|
|
},
|
|
|
|
|
2010-11-16 09:22:31 -08:00
|
|
|
/**
|
|
|
|
* Returns an object with the page's preferred viewport properties:
|
|
|
|
* defaultZoom (optional float): The initial scale when the page is loaded.
|
|
|
|
* minZoom (optional float): The minimum zoom level.
|
|
|
|
* maxZoom (optional float): The maximum zoom level.
|
|
|
|
* width (optional int): The CSS viewport width in px.
|
|
|
|
* height (optional int): The CSS viewport height in px.
|
|
|
|
* autoSize (boolean): Resize the CSS viewport when the window resizes.
|
|
|
|
* allowZoom (boolean): Let the user zoom in or out.
|
|
|
|
* autoScale (boolean): Adjust the viewport properties to account for display density.
|
|
|
|
*/
|
2010-06-09 16:07:12 -07:00
|
|
|
getViewportMetadata: function getViewportMetadata() {
|
|
|
|
let doctype = content.document.doctype;
|
|
|
|
if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId))
|
2010-11-16 09:22:31 -08:00
|
|
|
return { defaultZoom: 1, autoSize: true, allowZoom: true, autoScale: true };
|
2010-06-09 16:07:12 -07:00
|
|
|
|
|
|
|
let windowUtils = Util.getWindowUtils(content);
|
|
|
|
let handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly");
|
|
|
|
if (handheldFriendly == "true")
|
2010-11-16 09:22:31 -08:00
|
|
|
return { defaultZoom: 1, autoSize: true, allowZoom: true, autoScale: true };
|
2010-06-09 16:07:12 -07:00
|
|
|
|
|
|
|
if (content.document instanceof XULDocument)
|
2010-11-16 09:22:31 -08:00
|
|
|
return { defaultZoom: 1, autoSize: true, allowZoom: false, autoScale: false };
|
2010-06-09 16:07:12 -07:00
|
|
|
|
2010-09-16 13:17:09 -07:00
|
|
|
// HACK: Since we can't set the scale in local tabs (bug 597081), we force
|
|
|
|
// them to device-width and scale=1 so they will lay out reasonably.
|
|
|
|
if (Util.isParentProcess())
|
2010-11-16 09:22:31 -08:00
|
|
|
return { defaultZoom: 1, autoSize: true, allowZoom: false, autoScale: false };
|
2010-09-16 13:17:09 -07:00
|
|
|
|
2010-06-09 16:07:12 -07:00
|
|
|
// viewport details found here
|
|
|
|
// http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html
|
|
|
|
// http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html
|
|
|
|
|
|
|
|
// Note: These values will be NaN if parseFloat or parseInt doesn't find a number.
|
|
|
|
// Remember that NaN is contagious: Math.max(1, NaN) == Math.min(1, NaN) == NaN.
|
2010-11-16 09:22:31 -08:00
|
|
|
let scale = parseFloat(windowUtils.getDocumentMetadata("viewport-initial-scale"));
|
|
|
|
let minScale = parseFloat(windowUtils.getDocumentMetadata("viewport-minimum-scale"));
|
|
|
|
let maxScale = parseFloat(windowUtils.getDocumentMetadata("viewport-maximum-scale"));
|
|
|
|
|
|
|
|
let widthStr = windowUtils.getDocumentMetadata("viewport-width");
|
|
|
|
let heightStr = windowUtils.getDocumentMetadata("viewport-height");
|
|
|
|
let width = Util.clamp(parseInt(widthStr), kViewportMinWidth, kViewportMaxWidth);
|
|
|
|
let height = Util.clamp(parseInt(heightStr), kViewportMinHeight, kViewportMaxHeight);
|
|
|
|
|
|
|
|
let allowZoomStr = windowUtils.getDocumentMetadata("viewport-user-scalable");
|
|
|
|
let allowZoom = !/^(0|no|false)$/.test(allowZoomStr); // WebKit allows 0, "no", or "false"
|
2010-06-09 16:07:12 -07:00
|
|
|
|
2010-11-16 09:22:31 -08:00
|
|
|
scale = Util.clamp(scale, kViewportMinScale, kViewportMaxScale);
|
|
|
|
minScale = Util.clamp(minScale, kViewportMinScale, kViewportMaxScale);
|
|
|
|
maxScale = Util.clamp(maxScale, kViewportMinScale, kViewportMaxScale);
|
2010-06-09 16:07:12 -07:00
|
|
|
|
|
|
|
// If initial scale is 1.0 and width is not set, assume width=device-width
|
2010-11-16 09:22:31 -08:00
|
|
|
let autoSize = (widthStr == "device-width" ||
|
|
|
|
(!widthStr && (heightStr == "device-height" || scale == 1.0)));
|
2010-06-09 16:07:12 -07:00
|
|
|
|
|
|
|
return {
|
2010-11-16 09:22:31 -08:00
|
|
|
defaultZoom: scale,
|
|
|
|
minZoom: minScale,
|
|
|
|
maxZoom: maxScale,
|
|
|
|
width: width,
|
|
|
|
height: height,
|
2010-06-09 16:07:12 -07:00
|
|
|
autoSize: autoSize,
|
2010-11-16 09:22:31 -08:00
|
|
|
allowZoom: allowZoom,
|
|
|
|
autoScale: true
|
2010-06-09 16:07:12 -07:00
|
|
|
};
|
2010-06-17 09:27:26 -07:00
|
|
|
}
|
2010-06-09 16:07:12 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
ViewportHandler.init();
|
2010-06-25 09:32:46 -07:00
|
|
|
|
|
|
|
|
2010-06-29 11:15:07 -07:00
|
|
|
const kXLinkNamespace = "http://www.w3.org/1999/xlink";
|
|
|
|
|
|
|
|
var ContextHandler = {
|
2010-07-26 13:17:00 -07:00
|
|
|
_types: [],
|
|
|
|
|
2010-06-29 11:15:07 -07:00
|
|
|
_getLinkURL: function ch_getLinkURL(aLink) {
|
2010-09-15 23:28:12 -07:00
|
|
|
let href = aLink.href;
|
2010-06-29 11:15:07 -07:00
|
|
|
if (href)
|
|
|
|
return href;
|
|
|
|
|
|
|
|
href = aLink.getAttributeNS(kXLinkNamespace, "href");
|
|
|
|
if (!href || !href.match(/\S/)) {
|
|
|
|
// Without this we try to save as the current doc,
|
|
|
|
// for example, HTML case also throws if empty
|
|
|
|
throw "Empty href";
|
|
|
|
}
|
|
|
|
|
|
|
|
return Util.makeURLAbsolute(aLink.baseURI, href);
|
|
|
|
},
|
|
|
|
|
|
|
|
_getURI: function ch_getURI(aURL) {
|
|
|
|
try {
|
|
|
|
return Util.makeURI(aURL);
|
|
|
|
} catch (ex) { }
|
|
|
|
|
|
|
|
return null;
|
|
|
|
},
|
2010-07-12 16:29:03 -07:00
|
|
|
|
2010-06-29 11:15:07 -07:00
|
|
|
_getProtocol: function ch_getProtocol(aURI) {
|
|
|
|
if (aURI)
|
|
|
|
return aURI.scheme;
|
|
|
|
return null;
|
|
|
|
},
|
2010-07-12 16:29:03 -07:00
|
|
|
|
2010-06-29 11:15:07 -07:00
|
|
|
init: function ch_init() {
|
|
|
|
addEventListener("contextmenu", this, false);
|
2010-10-12 08:57:03 -07:00
|
|
|
addEventListener("pagehide", this, false);
|
2010-10-16 06:25:15 -07:00
|
|
|
addMessageListener("Browser:ContextCommand", this, false);
|
2010-10-12 08:57:03 -07:00
|
|
|
this.popupNode = null;
|
|
|
|
},
|
|
|
|
|
|
|
|
reset: function ch_reset() {
|
|
|
|
this.popupNode = null;
|
2010-06-29 11:15:07 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
handleEvent: function ch_handleEvent(aEvent) {
|
2010-10-12 08:57:03 -07:00
|
|
|
switch (aEvent.type) {
|
|
|
|
case "contextmenu":
|
|
|
|
this.onContextMenu(aEvent);
|
|
|
|
break;
|
|
|
|
case "pagehide":
|
|
|
|
this.reset();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onContextMenu: function ch_onContextMenu(aEvent) {
|
2010-07-12 16:29:03 -07:00
|
|
|
if (aEvent.getPreventDefault())
|
|
|
|
return;
|
|
|
|
|
2010-06-29 11:15:07 -07:00
|
|
|
let state = {
|
2010-07-28 21:13:31 -07:00
|
|
|
types: [],
|
|
|
|
label: "",
|
2010-06-29 11:15:07 -07:00
|
|
|
linkURL: "",
|
2010-07-29 21:51:31 -07:00
|
|
|
linkTitle: "",
|
2010-06-29 11:15:07 -07:00
|
|
|
linkProtocol: null,
|
|
|
|
mediaURL: ""
|
|
|
|
};
|
|
|
|
|
2010-10-12 08:57:03 -07:00
|
|
|
let popupNode = this.popupNode = aEvent.originalTarget;
|
2010-06-29 11:15:07 -07:00
|
|
|
|
|
|
|
// Do checks for nodes that never have children.
|
|
|
|
if (popupNode.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
|
|
|
|
// See if the user clicked on an image.
|
|
|
|
if (popupNode instanceof Ci.nsIImageLoadingContent && popupNode.currentURI) {
|
2010-07-28 21:13:31 -07:00
|
|
|
state.types.push("image");
|
2010-08-25 14:20:45 -07:00
|
|
|
state.label = state.mediaURL = popupNode.currentURI.spec;
|
2010-10-12 08:57:03 -07:00
|
|
|
} else if (popupNode instanceof Ci.nsIDOMHTMLMediaElement) {
|
|
|
|
state.label = state.mediaURL = (popupNode.currentSrc || popupNode.src);
|
|
|
|
state.types.push((popupNode.paused || popupNode.ended) ? "media-paused" : "media-playing");
|
|
|
|
if (popupNode instanceof Ci.nsIDOMHTMLVideoElement)
|
|
|
|
state.types.push("video");
|
2010-06-29 11:15:07 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let elem = popupNode;
|
|
|
|
while (elem) {
|
|
|
|
if (elem.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
|
|
|
|
// Link?
|
2010-07-28 21:13:31 -07:00
|
|
|
if ((elem instanceof Ci.nsIDOMHTMLAnchorElement && elem.href) ||
|
|
|
|
(elem instanceof Ci.nsIDOMHTMLAreaElement && elem.href) ||
|
|
|
|
elem instanceof Ci.nsIDOMHTMLLinkElement ||
|
|
|
|
elem.getAttributeNS(kXLinkNamespace, "type") == "simple") {
|
2010-06-29 11:15:07 -07:00
|
|
|
|
|
|
|
// Target is a link or a descendant of a link.
|
2010-07-28 21:13:31 -07:00
|
|
|
state.types.push("link");
|
|
|
|
state.label = state.linkURL = this._getLinkURL(elem);
|
2010-07-29 21:51:31 -07:00
|
|
|
state.linkTitle = popupNode.textContent || popupNode.title;
|
2010-06-29 11:15:07 -07:00
|
|
|
state.linkProtocol = this._getProtocol(this._getURI(state.linkURL));
|
2010-07-28 21:13:31 -07:00
|
|
|
break;
|
2010-06-29 11:15:07 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
elem = elem.parentNode;
|
|
|
|
}
|
2010-07-28 21:13:31 -07:00
|
|
|
|
|
|
|
for (let i = 0; i < this._types.length; i++)
|
|
|
|
if (this._types[i].handler(state, popupNode))
|
|
|
|
state.types.push(this._types[i].name);
|
2010-09-15 23:28:12 -07:00
|
|
|
|
2010-09-22 16:27:11 -07:00
|
|
|
state.messageId = this.messageId;
|
|
|
|
|
2010-06-29 11:15:07 -07:00
|
|
|
sendAsyncMessage("Browser:ContextMenu", state);
|
2010-07-28 21:13:31 -07:00
|
|
|
},
|
|
|
|
|
2010-10-12 08:57:03 -07:00
|
|
|
receiveMessage: function ch_receiveMessage(aMessage) {
|
2010-10-16 06:25:15 -07:00
|
|
|
let node = this.popupNode;
|
|
|
|
let command = aMessage.json.command;
|
|
|
|
|
|
|
|
switch (command) {
|
|
|
|
case "play":
|
|
|
|
case "pause":
|
|
|
|
if (node instanceof Ci.nsIDOMHTMLMediaElement)
|
|
|
|
node[command]();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "fullscreen":
|
|
|
|
if (node instanceof Ci.nsIDOMHTMLVideoElement) {
|
|
|
|
node.pause();
|
|
|
|
Cu.import("resource:///modules/video.jsm");
|
|
|
|
Video.fullScreenSourceElement = node;
|
|
|
|
sendAsyncMessage("Browser:FullScreenVideo:Start");
|
|
|
|
}
|
2010-10-12 08:57:03 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-07-28 21:13:31 -07:00
|
|
|
/**
|
|
|
|
* For add-ons to add new types and data to the ContextMenu message.
|
2010-09-15 23:28:12 -07:00
|
|
|
*
|
2010-07-28 21:13:31 -07:00
|
|
|
* @param aName A string to identify the new type.
|
|
|
|
* @param aHandler A function that takes a state object and a target element.
|
|
|
|
* If aHandler returns true, then aName will be added to the list of types.
|
|
|
|
* The function may also modify the state object.
|
|
|
|
*/
|
|
|
|
registerType: function registerType(aName, aHandler) {
|
|
|
|
this._types.push({name: aName, handler: aHandler});
|
2010-06-29 11:15:07 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-07-09 01:15:23 -07:00
|
|
|
ContextHandler.init();
|
2010-06-29 11:15:07 -07:00
|
|
|
|
2010-07-28 21:13:31 -07:00
|
|
|
ContextHandler.registerType("mailto", function(aState, aElement) {
|
|
|
|
return aState.linkProtocol == "mailto";
|
|
|
|
});
|
|
|
|
|
|
|
|
ContextHandler.registerType("callto", function(aState, aElement) {
|
|
|
|
let protocol = aState.linkProtocol;
|
|
|
|
return protocol == "tel" || protocol == "callto" || protocol == "sip" || protocol == "voipto";
|
|
|
|
});
|
|
|
|
|
|
|
|
ContextHandler.registerType("link-saveable", function(aState, aElement) {
|
|
|
|
let protocol = aState.linkProtocol;
|
|
|
|
return (protocol && protocol != "mailto" && protocol != "javascript" && protocol != "news" && protocol != "snews");
|
|
|
|
});
|
|
|
|
|
2010-11-03 06:48:01 -07:00
|
|
|
ContextHandler.registerType("link-openable", function(aState, aElement) {
|
|
|
|
let protocol = aState.linkProtocol;
|
|
|
|
return (protocol && protocol != "mailto" && protocol != "javascript" && protocol != "news" && protocol != "snews");
|
|
|
|
});
|
|
|
|
|
2010-08-23 17:27:40 -07:00
|
|
|
ContextHandler.registerType("link-shareable", function(aState, aElement) {
|
|
|
|
return Util.isShareableScheme(aState.linkProtocol);
|
|
|
|
});
|
|
|
|
|
2010-08-25 14:20:45 -07:00
|
|
|
["image", "video"].forEach(function(aType) {
|
|
|
|
ContextHandler.registerType(aType+"-shareable", function(aState, aElement) {
|
|
|
|
if (aState.types.indexOf(aType) == -1)
|
|
|
|
return false;
|
2010-08-23 17:27:40 -07:00
|
|
|
|
2010-08-25 14:20:45 -07:00
|
|
|
let protocol = ContextHandler._getProtocol(ContextHandler._getURI(aState.mediaURL));
|
|
|
|
return Util.isShareableScheme(protocol);
|
|
|
|
});
|
2010-08-23 17:27:40 -07:00
|
|
|
});
|
|
|
|
|
2010-07-28 21:13:31 -07:00
|
|
|
ContextHandler.registerType("image-loaded", function(aState, aElement) {
|
|
|
|
if (aState.types.indexOf("image") != -1) {
|
|
|
|
let request = aElement.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
|
|
|
|
if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
2010-06-29 11:15:07 -07:00
|
|
|
|
2010-06-25 09:32:46 -07:00
|
|
|
var FormSubmitObserver = {
|
2010-07-28 12:43:14 -07:00
|
|
|
init: function init(){
|
|
|
|
addMessageListener("Browser:TabOpen", this);
|
|
|
|
addMessageListener("Browser:TabClose", this);
|
|
|
|
},
|
|
|
|
|
|
|
|
receiveMessage: function findHandlerReceiveMessage(aMessage) {
|
|
|
|
let json = aMessage.json;
|
|
|
|
switch (aMessage.name) {
|
|
|
|
case "Browser:TabOpen":
|
|
|
|
Services.obs.addObserver(this, "formsubmit", false);
|
|
|
|
break;
|
|
|
|
case "Browser:TabClose":
|
|
|
|
Services.obs.removeObserver(this, "formsubmit", false);
|
|
|
|
break;
|
|
|
|
}
|
2010-06-25 09:32:46 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
notify: function notify(aFormElement, aWindow, aActionURI, aCancelSubmit) {
|
2010-07-28 12:43:14 -07:00
|
|
|
// Do not notify unless this is the window where the submit occurred
|
|
|
|
if (aWindow == content)
|
|
|
|
// We don't need to send any data along
|
|
|
|
sendAsyncMessage("Browser:FormSubmit", {});
|
2010-06-25 09:32:46 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
QueryInterface : function(aIID) {
|
|
|
|
if (!aIID.equals(Ci.nsIFormSubmitObserver) &&
|
|
|
|
!aIID.equals(Ci.nsISupportsWeakReference) &&
|
|
|
|
!aIID.equals(Ci.nsISupports))
|
|
|
|
throw Components.results.NS_ERROR_NO_INTERFACE;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-06-29 11:34:48 -07:00
|
|
|
FormSubmitObserver.init();
|
2010-07-19 07:51:03 -07:00
|
|
|
|
|
|
|
var FindHandler = {
|
|
|
|
get _fastFind() {
|
|
|
|
delete this._fastFind;
|
|
|
|
this._fastFind = Cc["@mozilla.org/typeaheadfind;1"].createInstance(Ci.nsITypeAheadFind);
|
|
|
|
this._fastFind.init(docShell);
|
|
|
|
return this._fastFind;
|
|
|
|
},
|
|
|
|
|
|
|
|
init: function findHandlerInit() {
|
|
|
|
addMessageListener("FindAssist:Find", this);
|
|
|
|
addMessageListener("FindAssist:Next", this);
|
|
|
|
addMessageListener("FindAssist:Previous", this);
|
|
|
|
},
|
|
|
|
|
|
|
|
receiveMessage: function findHandlerReceiveMessage(aMessage) {
|
|
|
|
let findResult = Ci.nsITypeAheadFind.FIND_NOTFOUND;
|
|
|
|
let json = aMessage.json;
|
|
|
|
switch (aMessage.name) {
|
|
|
|
case "FindAssist:Find":
|
|
|
|
findResult = this._fastFind.find(json.searchString, false);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "FindAssist:Previous":
|
|
|
|
findResult = this._fastFind.findAgain(true, false);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "FindAssist:Next":
|
|
|
|
findResult = this._fastFind.findAgain(false, false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (findResult == Ci.nsITypeAheadFind.FIND_NOTFOUND) {
|
|
|
|
sendAsyncMessage("FindAssist:Show", { rect: null , result: findResult });
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-09-21 05:29:00 -07:00
|
|
|
let selection = this._fastFind.currentWindow.getSelection();
|
2010-12-09 07:08:12 -08:00
|
|
|
if (!selection.rangeCount || selection.isCollapsed) {
|
2010-07-19 07:51:03 -07:00
|
|
|
// The selection can be into an input or a textarea element
|
|
|
|
let nodes = content.document.querySelectorAll("input[type='text'], textarea");
|
|
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
|
|
let node = nodes[i];
|
|
|
|
if (node instanceof Ci.nsIDOMNSEditableElement && node.editor) {
|
2010-09-21 05:29:00 -07:00
|
|
|
selection = node.editor.selectionController.getSelection(Ci.nsISelectionController.SELECTION_NORMAL);
|
2010-12-09 07:08:12 -08:00
|
|
|
if (selection.rangeCount && !selection.isCollapsed)
|
2010-07-19 07:51:03 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let scroll = Util.getScrollOffset(content);
|
2010-09-21 05:29:00 -07:00
|
|
|
for (let frame = this._fastFind.currentWindow; frame != content; frame = frame.parent) {
|
|
|
|
let rect = frame.frameElement.getBoundingClientRect();
|
|
|
|
let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth;
|
|
|
|
let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth;
|
|
|
|
scroll.add(rect.left + parseInt(left), rect.top + parseInt(top));
|
|
|
|
}
|
|
|
|
|
|
|
|
let rangeRect = selection.getRangeAt(0).getBoundingClientRect();
|
|
|
|
let rect = new Rect(scroll.x + rangeRect.left, scroll.y + rangeRect.top, rangeRect.width, rangeRect.height);
|
2010-10-25 07:35:13 -07:00
|
|
|
|
|
|
|
// Ensure the potential "scroll" event fired during a search as already fired
|
|
|
|
let timer = new Util.Timeout(function() {
|
|
|
|
sendAsyncMessage("FindAssist:Show", { rect: rect.isEmpty() ? null: rect , result: findResult });
|
|
|
|
});
|
|
|
|
timer.once(0);
|
2010-07-19 07:51:03 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
FindHandler.init();
|