Bug 568691 - component registration changes for mobile-browser, r=mfinkle

This commit is contained in:
Benjamin Smedberg 2010-07-01 14:33:53 -04:00
Родитель 6d92c5884e 43b87cf0ec
Коммит f4799e3157
345 изменённых файлов: 957 добавлений и 4147 удалений

Просмотреть файл

@ -474,3 +474,8 @@ pref("font.default.x-western", "SwissA");
// See bug 545869 for details on why these are set the way they are
pref("network.buffer.cache.count", 24);
pref("network.buffer.cache.size", 16384);
// sync service
pref("services.sync.client.type", "mobile");
pref("services.sync.registerEngines", "Tab,Bookmarks,Form,History,Password");
pref("services.sync.autoconnectDelay", 5);

Просмотреть файл

@ -481,8 +481,6 @@ BrowserView.prototype = {
this.updateScrolledArea(aMessage);
break;
}
return {};
},
updateDirtyTiles: function updateDirtyTiles(aMessage) {

Просмотреть файл

@ -138,7 +138,6 @@ function InputHandler(browserViewContainer) {
browserViewContainer.addEventListener("keydown", this, false);
browserViewContainer.addEventListener("DOMMouseScroll", this, true);
browserViewContainer.addEventListener("MozMousePixelScroll", this, true);
browserViewContainer.addEventListener("contextmenu", this, true);
this.addModule(new MouseModule(this, browserViewContainer));
this.addModule(new KeyModule(this, browserViewContainer));
@ -355,13 +354,15 @@ function MouseModule(owner, browserViewContainer) {
var self = this;
this._kinetic = new KineticController(Util.bind(this._dragBy, this),
Util.bind(this._kineticStop, this));
messageManager.addMessageListener("Browser:ContextMenu", this);
}
MouseModule.prototype = {
handleEvent: function handleEvent(evInfo) {
let evt = evInfo.event;
if (evt.button !== 0 && evt.type != "contextmenu")
if (evt.button !== 0)
return;
switch (evt.type) {
@ -374,17 +375,6 @@ MouseModule.prototype = {
case "mouseup":
this._onMouseUp(evInfo);
break;
case "contextmenu":
// TODO: Make "contextmenu" a first class part of InputHandler
// Bug 554639
if (ContextHelper.popupNode) {
if (this._clicker)
this._clicker.panBegin();
if (this._dragger)
this._dragger.dragStop(0, 0, this._targetScrollInterface);
this.cancelPending();
}
break;
case "MozMagnifyGestureStart":
case "MozMagnifyGesture":
// disallow kinetic panning after gesture
@ -394,6 +384,19 @@ MouseModule.prototype = {
}
},
receiveMessage: function receiveMessage(aMessage) {
// TODO: Make "contextmenu" a first class part of InputHandler
// Bug 554639
if (aMessage.name != "Browser:ContextMenu" || !ContextHelper.popupState)
return;
if (this._clicker)
this._clicker.panBegin();
if (this._dragger)
this._dragger.dragStop(0, 0, this._targetScrollInterface);
this.cancelPending();
},
/**
* This gets invoked by the input handler if another module grabs. We should
* reset our state or something here. This is probably doing the wrong thing

Просмотреть файл

@ -114,9 +114,9 @@ let Util = {
let link = null;
while (target) {
if (target instanceof HTMLAnchorElement ||
target instanceof HTMLAreaElement ||
target instanceof HTMLLinkElement) {
if (target instanceof Ci.nsIDOMHTMLAnchorElement ||
target instanceof Ci.nsIDOMHTMLAreaElement ||
target instanceof Ci.nsIDOMHTMLLinkElement) {
if (target.hasAttribute("href"))
link = target;
}
@ -129,9 +129,13 @@ let Util = {
return null;
},
makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) {
return gIOService.newURI(aURL, aOriginCharset, aBaseURI);
},
makeURLAbsolute: function makeURLAbsolute(base, url) {
// Note: makeURI() will throw if url is not a valid URI
return makeURI(url, null, makeURI(base)).spec;
return this.makeURI(url, null, this.makeURI(base)).spec;
},
clamp: function(num, min, max) {

Просмотреть файл

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
<!ENTITY % remoteTabsDTD SYSTEM "chrome://browser/locale/aboutTabs.dtd" >
%remoteTabsDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&remoteTabs.title;</title>
<link rel="icon" type="image/png" href="chrome://branding/content/favicon32.png" />
<link rel="stylesheet" href="chrome://browser/skin/aboutTabs.css" type="text/css"/>
</head>
<body onload="RemoteTabViewer.show();">
<div id="tabList">
</div>
<script type="application/javascript;version=1.8"><![CDATA[
// Make sure this is the only instance of the page
Components.utils.import("resource://services-sync/service.js");
Weave.Utils.ensureOneOpen(window);
const Cc = Components.classes;
const Ci = Components.interfaces;
let RemoteTabViewer = {
get chromeWin() {
let chromeWin = window.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShellTreeItem).
rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindow).QueryInterface(Ci.nsIDOMChromeWindow);
delete this.chromeWin;
return this.chromeWin = chromeWin;
},
show: function RemoteTabViewer_show() {
// Don't do anything if the tabs engine isn't ready
if (!Weave.Engines.get("tabs"))
return;
this._maybeNotify();
this._populateTabs();
this._refetchTabs();
},
_maybeNotify: function _maybeNotify() {
// Don't notify if the tab engine has new tabs or the user dismissed it
let prefs = Weave.Svc.Prefs;
if (prefs.get("notifyTabState") == 0)
return;
// No need to reshow the notification if it's still open
let notifyBox = this.chromeWin.getNotificationBox(window);
if (notifyBox.getNotificationWithValue("remote-tabs") != null)
return;
let message = Weave.Str.sync.get("remote.notification.label");
let notification = notifyBox.appendNotification(message, "remote-tabs", "",
notifyBox.PRIORITY_INFO_LOW);
// Wrap the close function to find out if the user clicks the X
let close = notification.close;
notification.close = function() {
// Once the user dismisses the dialog, remember that and don't show again
prefs.set("notifyTabState", 0);
close.apply(notification, arguments);
};
},
_refetchTabs: function _refetchTabs() {
// Don't bother refetching tabs if we already did so recently
let lastFetch = Weave.Svc.Prefs.get("lastTabFetch", 0);
let now = Math.floor(Date.now() / 1000);
if (now - lastFetch < 30)
return;
// Asynchronously fetch the tabs
setTimeout(function() {
let engine = Weave.Engines.get("tabs");
let lastSync = engine.lastSync;
// Force a sync only for the tabs engine
engine.lastModified = null;
engine.sync();
Weave.Svc.Prefs.set("lastTabFetch", now);
// Only reload the page if something synced
if (engine.lastSync != lastSync)
location.reload();
}, 0);
},
_populateTabs: function _populateTabs() {
// Clear out all child elements from holder first, so we don't
// end up adding duplicate rows.
let engine = Weave.Engines.get("tabs");
let holder = document.getElementById("tabList");
if (holder.hasChildNodes()) {
while (holder.childNodes.length >= 1)
holder.removeChild(holder.firstChild);
}
// Generate the list of tabs
let haveTabs = false;
for (let [guid, client] in Iterator(engine.getAllClients())) {
haveTabs = true;
// Create the client node, but don't add it in-case we don't show any tabs
let appendClient = true;
let nameNode = document.createElement("h2");
nameNode.textContent = client.clientName;
client.tabs.forEach(function({title, urlHistory, icon}) {
let pageUrl = urlHistory[0];
// Skip tabs that are already open
if (engine.locallyOpenTabMatchesURL(pageUrl))
return;
if (title == "")
title = pageUrl;
let item = document.createElement("div");
item.addEventListener("click", function() {
item.setAttribute("selected", true);
RemoteTabViewer.chromeWin.BrowserUI.newTab(pageUrl);
}, false)
item.setAttribute("class", "tab");
let img = document.createElement("img");
img.setAttribute("class", "icon");
img.src = Weave.Utils.getIcon(icon, "chrome://browser/skin/images/tab.png");
let tabDiv = document.createElement("div");
tabDiv.setAttribute("class", "info");
let titleNode = document.createElement("div");
titleNode.setAttribute("class", "title");
titleNode.textContent = title;
let urlNode = document.createElement("div");
urlNode.setAttribute("class", "url");
urlNode.textContent = pageUrl;
tabDiv.appendChild(titleNode);
tabDiv.appendChild(urlNode);
item.appendChild(img);
item.appendChild(tabDiv);
// Append the client name if we haven't yet
if (appendClient) {
appendClient = false;
holder.appendChild(nameNode);
}
holder.appendChild(item);
});
}
if (holder.childNodes.length == 0) {
// Assume we're pending, but we might already have tabs or have synced
let text = Weave.Str.sync.get("remote.pending.label");
if (haveTabs)
text = Weave.Str.sync.get("remote.opened.label");
else if (engine.lastSync != 0)
text = Weave.Str.sync.get("remote.missing.label");
let item = document.createElement("h1");
item.textContent = text;
document.getElementsByTagName('body')[0].appendChild(item);
}
}
};
]]></script>
</body>
</html>

Просмотреть файл

@ -255,7 +255,7 @@
let title = controller.getCommentAt(i);
let tags = '';
if (type == "tag")
if (title && type == "tag")
[, title, tags] = title.match(/^(.+) \u2013 (.+)$/);
item.setAttribute("tags", tags);

Просмотреть файл

@ -118,7 +118,7 @@
let link = aMessage.json;
// ignore results from subdocuments
if (link.windowId != this.contentWindowId)
return {};
return;
let linkType = this._getLinkType(link);
switch(linkType) {
@ -135,8 +135,6 @@
}
break;
}
return {};
]]></body>
</method>
@ -274,8 +272,6 @@
args);
break;
}
return {};
},
_notify: function(aFlags, aName, aArguments) {
@ -406,6 +402,27 @@
<field name="_webNavigation"><![CDATA[
({
LOAD_FLAGS_MASK: 65535,
LOAD_FLAGS_NONE: 0,
LOAD_FLAGS_IS_REFRESH: 16,
LOAD_FLAGS_IS_LINK: 32,
LOAD_FLAGS_BYPASS_HISTORY: 64,
LOAD_FLAGS_REPLACE_HISTORY: 128,
LOAD_FLAGS_BYPASS_CACHE: 256,
LOAD_FLAGS_BYPASS_PROXY: 512,
LOAD_FLAGS_CHARSET_CHANGE: 1024,
LOAD_FLAGS_STOP_CONTENT: 2048,
LOAD_FLAGS_FROM_EXTERNAL: 4096,
LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP: 8192,
LOAD_FLAGS_FIRST_LOAD: 16384,
LOAD_FLAGS_ALLOW_POPUPS: 32768,
LOAD_FLAGS_BYPASS_CLASSIFIER: 65536,
LOAD_FLAGS_FORCE_ALLOW_COOKIES: 131072,
STOP_NETWORK: 1,
STOP_CONTENT: 2,
STOP_ALL: 3,
canGoBack: false,
canGoForward: false,
goBack: function() { this._sendMessage("WebNavigation:GoBack", {}); },
@ -438,6 +455,12 @@
catch (e) {
Components.utils.reportError(e);
}
},
QueryInterface: function(aIID) {
if (aIID.equals(Components.interfaces.nsIWebNavigation) || aIID.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
}
})
]]></field>
@ -565,8 +588,6 @@
args);
break;
}
return {};
},
_notify: function(aFlags, aName, aArguments) {

Просмотреть файл

@ -119,8 +119,6 @@ var BrowserUI = {
break;
}
}
return { };
},
_titleChanged : function(aBrowser) {
@ -150,8 +148,6 @@ var BrowserUI = {
return { preventDefault: true };
}
}
return { };
},
_updateButtons : function(aBrowser) {
@ -388,9 +384,10 @@ var BrowserUI = {
messageManager.addMessageListener("DOMWillOpenModalDialog", this);
messageManager.addMessageListener("DOMWindowClose", this);
// listen returns messages from content
messageManager.addMessageListener("Browser:SaveAs:Return", this);
messageManager.addMessageListener("Browser:Highlight", this);
messageManager.addMessageListener("Browser:OpenURI", this);
messageManager.addMessageListener("Browser:ContextMenu", ContextHelper);
messageManager.addMessageListener("Browser:SaveAs:Return", this);
// listening mousedown for automatically dismiss some popups (e.g. larry)
window.addEventListener("mousedown", this, true);
@ -415,6 +412,9 @@ var BrowserUI = {
DownloadsView.init();
PreferencesView.init();
ConsoleView.init();
// Init the sync system
WeaveGlue.init();
});
FormMessageReceiver.start();
@ -815,9 +815,10 @@ var BrowserUI = {
}
TapHighlightHelper.show(rects);
break;
}
return {};
case "Browser:OpenURI":
Browser.addTab(json.uri, false, Browser.selectedTab);
}
},
supportsCommand : function(cmd) {
@ -2003,135 +2004,40 @@ var SelectHelper = {
const kXLinkNamespace = "http://www.w3.org/1999/xlink";
var ContextHelper = {
popupNode: null,
onLink: false,
onSaveableLink: false,
onVoiceLink: false,
onImage: false,
onLoadedImage: false,
linkURL: "",
linkProtocol: null,
mediaURL: "",
popupState: null,
_clearState: function ch_clearState() {
this.popupNode = null;
this.onLink = false;
this.onSaveableLink = false;
this.onVoiceLink = false;
this.onImage = false;
this.onLoadedImage = false;
this.linkURL = "";
this.linkProtocol = null;
this.mediaURL = "";
},
receiveMessage: function ch_receiveMessage(aMessage) {
this.popupState = aMessage.json;
this.popupState.browser = aMessage.target;
_getLinkURL: function ch_getLinkURL(aLink) {
let href = aLink.href;
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 makeURI(aURL);
} catch (ex) { }
return null;
},
_getProtocol: function ch_getProtocol(aURI) {
if (aURI)
return aURI.scheme;
return null;
},
_isSaveable: function ch_isSaveable(aProtocol) {
// We don't do the Right Thing for news/snews yet, so turn them off until we do
return aProtocol && !(aProtocol == "mailto" || aProtocol == "javascript" || aProtocol == "news" || aProtocol == "snews");
},
_isVoice: function ch_isVoice(aProtocol) {
// Collection of protocols related to voice or data links
return aProtocol && (aProtocol == "tel" || aProtocol == "callto" || aProtocol == "sip" || aProtocol == "voipto");
},
handleEvent: function ch_handleEvent(aEvent) {
this._clearState();
let [elementX, elementY] = Browser.transformClientToBrowser(aEvent.clientX, aEvent.clientY);
this.popupNode = Browser.elementFromPoint(elementX, elementY);
// Do checks for nodes that never have children.
if (this.popupNode.nodeType == Node.ELEMENT_NODE) {
// See if the user clicked on an image.
if (this.popupNode instanceof Ci.nsIImageLoadingContent && this.popupNode.currentURI) {
this.onImage = true;
this.mediaURL = this.popupNode.currentURI.spec;
let request = this.popupNode.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
this.onLoadedImage = true;
}
}
let elem = this.popupNode;
while (elem) {
if (elem.nodeType == Node.ELEMENT_NODE) {
// Link?
if (!this.onLink &&
((elem instanceof HTMLAnchorElement && elem.href) ||
(elem instanceof HTMLAreaElement && elem.href) ||
elem instanceof HTMLLinkElement ||
elem.getAttributeNS(kXLinkNamespace, "type") == "simple")) {
// Target is a link or a descendant of a link.
this.linkURL = this._getLinkURL(elem);
this.linkProtocol = this._getProtocol(this._getURI(this.linkURL));
this.onLink = true;
this.onSaveableLink = this._isSaveable(this.linkProtocol);
this.onVoiceLink = this._isVoice(this.linkProtocol);
}
}
elem = elem.parentNode;
}
let first = last = null;
let first = null;
let last = null;
let commands = document.getElementById("context-commands");
for (let i=0; i<commands.childElementCount; i++) {
let command = commands.children[i];
let types = command.getAttribute("type").split(/\s+/);
command.removeAttribute("selector");
if (types.indexOf("image") != -1 && this.onImage) {
if (types.indexOf("image") != -1 && this.popupState.onImage) {
first = (first ? first : command);
last = command;
command.hidden = false;
continue;
} else if (types.indexOf("image-loaded") != -1 && this.onLoadedImage) {
} else if (types.indexOf("image-loaded") != -1 && this.popupState.onLoadedImage) {
first = (first ? first : command);
last = command;
command.hidden = false;
continue;
} else if (types.indexOf("link") != -1 && this.onSaveableLink) {
} else if (types.indexOf("link") != -1 && this.popupState.onSaveableLink) {
first = (first ? first : command);
last = command;
command.hidden = false;
continue;
} else if (types.indexOf("callto") != -1 && this.onVoiceLink) {
} else if (types.indexOf("callto") != -1 && this.popupState.onVoiceLink) {
first = (first ? first : command);
last = command;
command.hidden = false;
continue;
} else if (types.indexOf("mailto") != -1 && this.onLink && this.linkProtocol == "mailto") {
} else if (types.indexOf("mailto") != -1 && this.popupState.onLink && this.popupState.linkProtocol == "mailto") {
first = (first ? first : command);
last = command;
command.hidden = false;
@ -2141,7 +2047,7 @@ var ContextHelper = {
}
if (!first) {
this._clearState();
this.popupState = null;
return;
}
@ -2149,10 +2055,10 @@ var ContextHelper = {
last.setAttribute("selector", "last-child");
let label = document.getElementById("context-hint");
if (this.onImage)
label.value = this.mediaURL;
if (this.onLink)
label.value = this.linkURL;
if (this.popupState.onImage)
label.value = this.popupState.mediaURL;
if (this.popupState.onLink)
label.value = this.popupState.linkURL;
let container = document.getElementById("context-popup");
container.hidden = false;
@ -2176,7 +2082,7 @@ var ContextHelper = {
},
hide: function ch_hide() {
this._clearState();
this.popupState = null;
let container = document.getElementById("context-popup");
container.hidden = true;
@ -2187,12 +2093,12 @@ var ContextHelper = {
var ContextCommands = {
openInNewTab: function cc_openInNewTab(aEvent) {
Browser.addTab(ContextHelper.linkURL, false, Browser.selectedTab);
Browser.addTab(ContextHelper.popupState.linkURL, false, Browser.selectedTab);
},
saveImage: function cc_saveImage(aEvent) {
let doc = ContextHelper.popupNode.ownerDocument;
saveImageURL(ContextHelper.mediaURL, null, "SaveImageTitle", false, false, doc.documentURIObject);
let browser = ContextHelper.popupState.browser;
saveImageURL(ContextHelper.popupState.mediaURL, null, "SaveImageTitle", false, false, browser.documentURI);
}
}

Просмотреть файл

@ -476,14 +476,11 @@ var Browser = {
notifications.addEventListener("AlertActive", notificationHandler, false);
notifications.addEventListener("AlertClose", notificationHandler, false);
// Add context helper to the content area only
container.addEventListener("contextmenu", ContextHelper, false);
BrowserUI.init();
// initialize input handling
ih = new InputHandler(container);
BrowserUI.init();
window.controllers.appendController(this);
window.controllers.appendController(BrowserUI);
@ -691,7 +688,8 @@ var Browser = {
if (aScrollY != 0)
Browser.hideTitlebar();
Browser.contentScrollboxScroller.scrollTo(aScrollX, aScrollY);
let zoomLevel = this._browserView.getZoomLevel();
Browser.contentScrollboxScroller.scrollTo(aScrollX*zoomLevel, aScrollY*zoomLevel);
this._browserView.onAfterVisibleMove();
},
@ -1277,7 +1275,10 @@ var Browser = {
switch (aMessage.name) {
case "Browser:ViewportMetadata":
let tab = Browser.getTabForBrowser(aMessage.target);
tab.updateViewportMetadata(json);
// Some browser such as iframes loaded dynamically into the chrome UI
// does not have any assigned tab
if (tab)
tab.updateViewportMetadata(json);
break;
case "Browser:FormSubmit":
@ -1304,14 +1305,8 @@ Browser.MainDragger.prototype = {
isDraggable: function isDraggable(target, scroller) { return true; },
dragStart: function dragStart(clientX, clientY, target, scroller) {
// Make sure pausing occurs before any early returns.
this.bv.pauseRendering();
// XXX shouldn't know about observer
// adding pause in pauseRendering isn't so great, because tiles will hardly ever prefetch while
// loading state is going (and already, the idle timer is bigger during loading so it doesn't fit
// into the aggressive flag).
this.bv._idleServiceObserver.pause();
this._nextRender = Date.now() + 500;
this._dragMoved = false;
},
dragStop: function dragStop(dx, dy, scroller) {
@ -1320,15 +1315,17 @@ Browser.MainDragger.prototype = {
Browser.tryUnfloatToolbar();
this.bv.resumeRendering();
// XXX shouldn't know about observer
this.bv._idleServiceObserver.resume();
if (this._dragMoved)
this.bv.resumeRendering();
},
dragMove: function dragMove(dx, dy, scroller) {
let doffset = new Point(dx, dy);
let render = false;
if (!this._dragMoved) {
this._dragMoved = true;
this.bv.pauseRendering();
}
// First calculate any panning to take sidebars out of view
let panOffset = this._panControlsAwayOffset(doffset);
@ -1345,8 +1342,10 @@ Browser.MainDragger.prototype = {
this.bv.onAfterVisibleMove();
if (render)
if (Date.now() >= this._nextRender) {
this.bv.renderNow();
this._nextRender = Date.now() + 500;
}
return !doffset.equals(dx, dy);
},
@ -1555,10 +1554,13 @@ function ContentCustomClicker(browserView) {
}
ContentCustomClicker.prototype = {
_dispatchMouseEvent: function _dispatchMouseEvent(aName, aX, aY) {
_dispatchMouseEvent: function _dispatchMouseEvent(aName, aX, aY, aModifiers) {
let aX = aX || 0;
let aY = aY || 0;
let aModifiers = aModifiers || null;
let browser = this._browserView.getBrowser();
let [x, y] = Browser.transformClientToBrowser(aX, aY);
browser.messageManager.sendAsyncMessage(aName, { x: x, y: y });
browser.messageManager.sendAsyncMessage(aName, { x: x, y: y, modifiers: aModifiers });
},
mouseDown: function mouseDown(aX, aY) {
@ -1576,33 +1578,30 @@ ContentCustomClicker.prototype = {
panBegin: function panBegin() {
TapHighlightHelper.hide();
let browser = this._browserView.getBrowser();
browser.messageManager.sendAsyncMessage("Browser:MouseCancel", {});
this._dispatchMouseEvent("Browser:MouseCancel");
},
singleClick: function singleClick(aX, aY, aModifiers) {
TapHighlightHelper.hide();
this._dispatchMouseEvent("Browser:MouseUp", aX, aY);
// TODO e10s: handle modifiers for clicks
//
// if (modifiers == Ci.nsIDOMNSEvent.CONTROL_MASK) {
// let uri = Util.getHrefForElement(element);
// if (uri)
// Browser.addTab(uri, false);
// }
// Cancel the mouse click if we are showing a context menu
if (!ContextHelper.popupState)
this._dispatchMouseEvent("Browser:MouseUp", aX, aY, aModifiers);
this._dispatchMouseEvent("Browser:MouseCancel");
},
doubleClick: function doubleClick(aX1, aY1, aX2, aY2) {
TapHighlightHelper.hide();
let browser = this._browserView.getBrowser();
browser.messageManager.sendAsyncMessage("Browser:MouseCancel", {});
this._dispatchMouseEvent("Browser:MouseCancel");
const kDoubleClickRadius = 32;
let maxRadius = kDoubleClickRadius * Browser._browserView.getZoomLevel();
let isClickInRadius = (Math.abs(aX1 - aX2) < maxRadius && Math.abs(aY1 - aY2) < maxRadius);
if (isClickInRadius)
browser.messageManager.sendAsyncMessage("Browser:ZoomToPoint", { x: aX1, y: aY1 });
this._dispatchMouseEvent("Browser:ZoomToPoint", aX1, aY1);
},
toString: function toString() {

Просмотреть файл

@ -86,6 +86,7 @@
<script type="application/javascript" src="chrome://browser/content/TileManager.js"/>
<script type="application/javascript" src="chrome://browser/content/BrowserView.js"/>
<script type="application/javascript" src="chrome://browser/content/AnimatedZoom.js"/>
<script type="application/javascript" src="chrome://browser/content/sync.js"/>
<stringbundleset id="stringbundleset">
<stringbundle id="bundle_browser" src="chrome://browser/locale/browser.properties"/>
@ -113,6 +114,7 @@
<!-- tabs -->
<command id="cmd_newTab" label="&newtab.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_closeTab" label="&closetab.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_remoteTabs" oncommand="WeaveGlue.openRemoteTabs();"/>
<!-- bookmarking -->
<command id="cmd_star" label="&star.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
@ -202,6 +204,7 @@
<vbox id="tabs" onselect="BrowserUI.selectTab(this);" onclosetab="BrowserUI.closeTab(this)" flex="1"/>
<hbox id="tabs-controls">
<toolbarbutton id="newtab-button" class="button-image" command="cmd_newTab"/>
<toolbarbutton id="remotetabs-button" class="button-image" disabled="true" command="cmd_remoteTabs"/>
</hbox>
</vbox>
</vbox>
@ -257,7 +260,7 @@
<stack id="tile-stack" class="window-width" flex="1">
<scrollbox id="content-scrollbox" style="overflow: hidden;" class="window-width" flex="1">
<!-- Content viewport -->
<html:div id="tile-container" style="overflow: hidden;">
<html:div id="tile-container" style="overflow: hidden;" tabindex="-1">
<html:canvas id="content-overlay" style="display: none; position: absolute; z-index: 1000; left: 0; top: 0;">
</html:canvas>
</html:div>
@ -431,6 +434,21 @@
<button id="prefs-clear-data" label="&clearPrivateData.button;" command="cmd_sanitize"/>
</setting>
</settings>
<settings id="prefs-sync" label="&sync.title;">
<setting id="sync-user" type="string" title="&sync.username;" />
<setting id="sync-pass" type="string" inputtype="password" title="&sync.password;" />
<setting id="sync-secret" type="string" inputtype="password" title="&sync.secretPhrase;" />
<setting id="sync-device" type="string" title="&sync.deviceName;" onchange="WeaveGlue.changeName(this)" collapsed="true"/>
<setting id="sync-connect" type="control">
<button label="&sync.connect;" oncommand="WeaveGlue.connect();" />
</setting>
<setting id="sync-disconnect" type="control" collapsed="true">
<button label="&sync.disconnect;" oncommand="WeaveGlue.disconnect();" />
</setting>
<setting id="sync-sync" type="control" collapsed="true">
<button id="sync-syncButton" label="&sync.syncNow;" oncommand="WeaveGlue.sync();" />
</setting>
</settings>
</richlistbox>
</notificationbox>
</vbox>

Просмотреть файл

@ -12,6 +12,8 @@ let gPrefService = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch2);
let gObserverService = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
let gIOService = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
let XULDocument = Ci.nsIDOMXULDocument;
let HTMLHtmlElement = Ci.nsIDOMHTMLHtmlElement;
@ -443,6 +445,7 @@ Content.prototype = {
let json = aMessage.json;
let x = json.x;
let y = json.y;
let modifiers = json.modifiers;
switch (aMessage.name) {
case "Browser:Blur":
@ -459,25 +462,38 @@ Content.prototype = {
if (this._overlayTimeout)
return;
this._overlayTimeout = content.setTimeout(function() {
let element = elementFromPoint(x, y);
if (!element || !element.mozMatchesSelector("*:link,*:visited,*:link *,*:visited *,*[role=button],button,input,option,select,textarea,label"))
return;
let element = elementFromPoint(x, y);
if (!element)
return;
let rects = getContentClientRects(element);
sendAsyncMessage("Browser:Highlight", { rects: rects });
}, kTapOverlayTimeout);
this._sendMouseEvent("mousedown", element, x, y);
// If we don't release the implicit capture, we'll get dragging problems
// when a contextmenu is displayed
element.ownerDocument.releaseCapture();
if (element.mozMatchesSelector("*:link,*:visited,*:link *,*:visited *,*[role=button],button,input,option,select,textarea,label")) {
this._overlayTimeout = content.setTimeout(function() {
let rects = getContentClientRects(element);
sendSyncMessage("Browser:Highlight", { rects: rects });
}, kTapOverlayTimeout);
}
break;
case "Browser:MouseUp": {
let element = elementFromPoint(x, y);
if (!this._formAssistant.open(element)) {
this._sendMouseEvent("mousedown", element, x, y);
if (modifiers == Ci.nsIDOMNSEvent.CONTROL_MASK) {
let uri = Util.getHrefForElement(element);
if (uri)
sendAsyncMessage("Browser:OpenURI", { uri: uri });
} else if (!this._formAssistant.open(element)) {
this._sendMouseEvent("mouseup", element, x, y);
}
break;
}
case "Browser:MouseCancel":
this._cancelMouseEvent();
if (this._overlayTimeout) {
content.clearTimeout(this._overlayTimeout);
this._overlayTimeout = 0;
@ -538,10 +554,6 @@ Content.prototype = {
},
_sendMouseEvent: function _sendMouseEvent(aName, aElement, aX, aY) {
let scrollOffset = Util.getScrollOffset(content);
aX -= scrollOffset.x;
aY -= scrollOffset.y;
// the element can be out of the aX/aY point because of the touch radius
if (!(aElement instanceof HTMLHtmlElement)) {
let isTouchClick = true;
@ -563,8 +575,17 @@ Content.prototype = {
}
}
let scrollOffset = Util.getScrollOffset(content);
let windowUtils = Util.getWindowUtils(content);
windowUtils.sendMouseEvent(aName, aX, aY, 0, 1, 0, true);
windowUtils.sendMouseEvent(aName, aX - scrollOffset.x, aY - scrollOffset.y, 0, 1, 0, true);
},
_cancelMouseEvent: function _cancelMouseEvent() {
// We use a mouseup with a clickcount=0 to cancel the contextmenu timer in
// nsEventStateManager.cpp
let scrollOffset = Util.getScrollOffset(content);
let windowUtils = Util.getWindowUtils(content);
windowUtils.sendMouseEvent("mouseup", scrollOffset.x, scrollOffset.y, 0, 0, 0, true);
},
startLoading: function startLoading() {
@ -690,6 +711,108 @@ let ViewportHandler = {
ViewportHandler.init();
const kXLinkNamespace = "http://www.w3.org/1999/xlink";
var ContextHandler = {
_getLinkURL: function ch_getLinkURL(aLink) {
let href = aLink.href;
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;
},
_getProtocol: function ch_getProtocol(aURI) {
if (aURI)
return aURI.scheme;
return null;
},
_isSaveable: function ch_isSaveable(aProtocol) {
// We don't do the Right Thing for news/snews yet, so turn them off until we do
return aProtocol && !(aProtocol == "mailto" || aProtocol == "javascript" || aProtocol == "news" || aProtocol == "snews");
},
_isVoice: function ch_isVoice(aProtocol) {
// Collection of protocols related to voice or data links
return aProtocol && (aProtocol == "tel" || aProtocol == "callto" || aProtocol == "sip" || aProtocol == "voipto");
},
init: function ch_init() {
addEventListener("contextmenu", this, false);
},
handleEvent: function ch_handleEvent(aEvent) {
let state = {
onLink: false,
onSaveableLink: false,
onVoiceLink: false,
onImage: false,
onLoadedImage: false,
linkURL: "",
linkProtocol: null,
mediaURL: ""
};
let popupNode = elementFromPoint(aEvent.clientX, aEvent.clientY);
// 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) {
state.onImage = true;
state.mediaURL = popupNode.currentURI.spec;
let request = popupNode.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
state.onLoadedImage = true;
}
}
let elem = popupNode;
while (elem) {
if (elem.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
// Link?
if (!this.onLink &&
((elem instanceof Ci.nsIDOMHTMLAnchorElement && elem.href) ||
(elem instanceof Ci.nsIDOMHTMLAreaElement && elem.href) ||
elem instanceof Ci.nsIDOMHTMLLinkElement ||
elem.getAttributeNS(kXLinkNamespace, "type") == "simple")) {
// Target is a link or a descendant of a link.
state.linkURL = this._getLinkURL(elem);
state.linkProtocol = this._getProtocol(this._getURI(state.linkURL));
state.onLink = true;
state.onSaveableLink = this._isSaveable(state.linkProtocol);
state.onVoiceLink = this._isVoice(state.linkProtocol);
}
}
elem = elem.parentNode;
}
sendAsyncMessage("Browser:ContextMenu", state);
}
};
ContextHandler.init();
var FormSubmitObserver = {
init: function init() {
gObserverService.addObserver(this, "formsubmit", false);
@ -709,4 +832,4 @@ var FormSubmitObserver = {
}
};
FormSubmitObserver.init();
FormSubmitObserver.init();

Просмотреть файл

@ -181,7 +181,6 @@ FormAssistant.prototype = {
break;
default:
let target = aEvent.target;
if (currentWrapper.canAutocomplete())
sendAsyncMessage("FormAssist:AutoComplete", this.getJSON());
break;
@ -382,6 +381,10 @@ BasicWrapper.prototype = {
autocomplete: function(aValue) {
this.element.value = aValue;
let event = this.element.ownerDocument.createEvent("Events");
event.initEvent("DOMAutoComplete", true, true);
this.element.dispatchEvent(event);
},
/** Caret is used to input text for this element. */
@ -571,7 +574,7 @@ SelectWrapper.prototype = {
evt.initEvent("change", true, true, this._control.ownerDocument.defaultView, 0,
false, false,
false, false, null);
content.document.defaultView.setTimeout(function() {
content.setTimeout(function() {
control.dispatchEvent(evt);
}, 0);
}
@ -632,7 +635,7 @@ MenulistWrapper.prototype = {
evt.initCommandEvent("command", true, true, window, 0,
false, false,
false, false, null);
content.document.defaultView.setTimeout(function() {
content.setTimeout(function() {
control.dispatchEvent(evt);
}, 0);
}

Просмотреть файл

@ -0,0 +1,188 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Bookmarks sync code.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jono DiCarlo <jdicarlo@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let WeaveGlue = {
init: function init() {
Components.utils.import("resource://services-sync/service.js");
this._addListeners();
// Initialize the UI now
this._updateOptions();
// Generating keypairs is expensive on mobile, so disable it
Weave.Service.keyGenEnabled = false;
},
openRemoteTabs: function openRemoteTabs() {
this._openTab("about:sync-tabs");
},
connect: function connect() {
Weave.Service.login(this._settings.user.value, this._settings.pass.value,
this._settings.secret.value);
Weave.Service.persistLogin();
},
disconnect: function disconnect() {
Weave.Service.logout();
},
sync: function sync() {
Weave.Service.sync();
},
_addListeners: function _addListeners() {
let topics = ["weave:service:sync:start", "weave:service:sync:finish",
"weave:service:sync:error", "weave:service:login:start",
"weave:service:login:finish", "weave:service:login:error",
"weave:service:logout:finish"];
// For each topic, add or remove _updateOptions as the observer
let addRem = function(add) topics.forEach(function(topic) Weave.Svc.
Obs[add ? "add" : "remove"](topic, WeaveGlue._updateOptions, WeaveGlue));
// Add the listeners now, and remove them on unload
addRem(true);
addEventListener("unload", function() addRem(false), false);
},
_openTab: function _openTab(url) {
setTimeout(function() BrowserUI.newTab(url), 0);
},
get _settings() {
// Do a quick test to see if the options exist yet
let syncButton = document.getElementById("sync-syncButton");
if (syncButton == null)
return;
// Get all the setting nodes from the add-ons display
let settings = {};
let ids = ["user", "pass", "secret", "device", "connect", "disconnect", "sync"];
ids.forEach(function(id) {
settings[id] = document.getElementById("sync-" + id);
});
// Replace the getter with the collection of settings
delete this._settings;
return this._settings = settings;
},
_updateOptions: function _updateOptions() {
let loggedIn = Weave.Service.isLoggedIn;
document.getElementById("remotetabs-button").disabled = !loggedIn;
// Make sure we're online when connecting/syncing
Util.forceOnline();
// Can't do anything before settings are loaded
if (this._settings == null)
return;
// Make some aliases
let user = this._settings.user;
let pass = this._settings.pass;
let secret = this._settings.secret;
let connect = this._settings.connect;
let device = this._settings.device;
let disconnect = this._settings.disconnect;
let sync = this._settings.sync;
let syncStr = Weave.Str.sync;
// Make sure the options are in the right state
user.collapsed = loggedIn;
pass.collapsed = loggedIn;
secret.collapsed = loggedIn;
connect.collapsed = loggedIn;
device.collapsed = !loggedIn;
disconnect.collapsed = !loggedIn;
sync.collapsed = !loggedIn;
// Check the lock on a timeout because it's set just after notifying
setTimeout(Weave.Utils.bind2(this, function() {
// Prevent certain actions when the service is locked
if (Weave.Service.locked) {
connect.firstChild.disabled = true;
sync.firstChild.disabled = true;
connect.setAttribute("title", syncStr.get("connecting.label"));
sync.setAttribute("title", syncStr.get("lastSyncInProgress.label"));
} else {
connect.firstChild.disabled = false;
sync.firstChild.disabled = false;
connect.setAttribute("title", syncStr.get("disconnected.label"));
}
}), 0);
// Move the disconnect and sync settings out to make connect the last item
let parent = connect.parentNode;
if (!loggedIn)
parent = parent.parentNode;
parent.appendChild(disconnect);
parent.appendChild(sync);
// Dynamically generate some strings
let connectedStr = syncStr.get("connected.label", [Weave.Service.username]);
disconnect.setAttribute("title", connectedStr);
// Show the day-of-week and time (HH:MM) of last sync
let lastSync = Weave.Svc.Prefs.get("lastSync");
if (lastSync != null) {
let syncDate = new Date(lastSync).toLocaleFormat("%a %R");
let dateStr = syncStr.get("lastSync.label", [syncDate]);
sync.setAttribute("title", dateStr);
}
// Show what went wrong with login if necessary
let login = Weave.Status.login;
if (login == Weave.LOGIN_SUCCEEDED)
connect.removeAttribute("desc");
else if (login != null)
connect.setAttribute("desc", Weave.Str.errors.get(login));
// Load the values for the string inputs
user.value = Weave.Service.username || "";
pass.value = Weave.Service.password || "";
secret.value = Weave.Service.passphrase || "";
device.value = Weave.Clients.localName || "";
},
changeName: function changeName(input) {
// Make sure to update to a modified name, e.g., empty-string -> default
Weave.Clients.localName = input.value;
input.value = Weave.Clients.localName;
}
};

Просмотреть файл

@ -16,6 +16,7 @@ chrome.jar:
content/aboutCertError.xhtml (content/aboutCertError.xhtml)
content/aboutCertError.css (content/aboutCertError.css)
content/aboutHome.xhtml (content/aboutHome.xhtml)
content/aboutTabs.xhtml (content/aboutTabs.xhtml)
content/languages.properties (content/languages.properties)
* content/browser.xul (content/browser.xul)
* content/browser.js (content/browser.js)
@ -58,5 +59,6 @@ chrome.jar:
content/prompt/select.xul (content/prompt/select.xul)
content/prompt/prompt.js (content/prompt/prompt.js)
content/AnimatedZoom.js (content/AnimatedZoom.js)
content/sync.js (content/sync.js)
% override chrome://global/content/config.xul chrome://browser/content/config.xul

Просмотреть файл

@ -52,9 +52,7 @@ function checkClick() {
element = newTab.browser.contentDocument.documentElement;
element.addEventListener("click", clickFired, true);
finish(); // XXX Browser.getBoundingContentRect not available.
let rect = Browser.getBoundingContentRect(element);
let rect = getBoundingContentRect(element);
EventUtils.synthesizeMouse(element, 1, rect.height + 10, {}, window);
waitFor(checkPosition, function() { return isClickFired });
}
@ -62,7 +60,7 @@ function checkClick() {
function checkPosition() {
element.removeEventListener("click", clickFired, true);
let rect = Browser.getBoundingContentRect(element);
let rect = getBoundingContentRect(element);
is(clickPosition.x, 1, "X position is correct");
is(clickPosition.y, rect.height + 10, "Y position is correct");
@ -73,11 +71,11 @@ function checkThickBorder() {
let frame = newTab.browser.contentDocument.getElementById("iframe-2");
let element = frame.contentDocument.getElementsByTagName("input")[0];
let frameRect = Browser.getBoundingContentRect(frame);
let frameRect = getBoundingContentRect(frame);
let frameLeftBorder = window.getComputedStyle(frame, "").borderLeftWidth;
let frameTopBorder = window.getComputedStyle(frame, "").borderTopWidth;
let elementRect = Browser.getBoundingContentRect(element);
let elementRect = getBoundingContentRect(element);
ok((frameRect.left + parseInt(frameLeftBorder)) < elementRect.left, "X position of nested element ok");
ok((frameRect.top + parseInt(frameTopBorder)) < elementRect.top, "Y position of nested element ok");
@ -91,3 +89,27 @@ function close() {
// We must finialize the tests
finish();
}
// XXX copied from chrome/content/content.js
function getBoundingContentRect(aElement) {
if (!aElement)
return new Rect(0, 0, 0, 0);
let document = aElement.ownerDocument;
while(document.defaultView.frameElement)
document = document.defaultView.frameElement.ownerDocument;
let offset = Util.getScrollOffset(content);
let r = aElement.getBoundingClientRect();
// 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));
}
return new Rect(r.left + offset.x, r.top + offset.y, r.width, r.height);
}

Просмотреть файл

@ -67,8 +67,8 @@ gTests.push({
let controls = document.getElementById("controls-scrollbox");
// Assign offsets while panning
initialDragOffset = document.getElementById("tabs-container").getBoundingClientRect().width;
finalDragOffset = initialDragOffset + document.getElementById("browser-controls")
initialDragOffset = document.getElementById("tabs-container").getBoundingClientRect().width;
finalDragOffset = initialDragOffset + document.getElementById("browser-controls")
.getBoundingClientRect().width;
gCurrentTest._contentScrollbox.getPosition(x,y);
@ -91,12 +91,12 @@ gTests.push({
is(prefsOpen.checked, false, "Preferences open button must not be depressed");
// check if preferences pane is invisble
is(document.getElementById("panel-container").hidden,true, "Preferences panel is invisble");
is(BrowserUI.isPanelVisible(), false, "Preferences panel is invisble");
// click on the prefs button to go the preferences pane
var prefsClick = document.getElementById("tool-panel-open");
prefsClick.click();
waitFor(gCurrentTest.onPrefsView, function() { return document.getElementById("panel-container").hidden == false; });
waitFor(gCurrentTest.onPrefsView, BrowserUI.isPanelVisible);
},
onPrefsView: function(){
@ -105,8 +105,7 @@ gTests.push({
let h = prefsList.clientHeight;
//check whether the preferences panel is visible
var prefsContainer = document.getElementById("panel-container");
is(prefsContainer.hidden, false, "Preferences panel must now be visble");
ok(BrowserUI.isPanelVisible(), "Preferences panel must now be visble");
// Check if preferences container is visible
is(document.getElementById("panel-container").hidden, false, "Preferences panel should be visible");
@ -133,9 +132,10 @@ gTests.push({
// Move preferences pane upexpected "+ finalDragOffset +"
dragElement(prefsList,w/2,h/2,w/2,h/4);
// Check whether it is moved up to the correct view, height should be 104
// Check whether it is moved up to the correct view
let distance = (h/2) - (h/4);
gCurrentTest._prefsScrollbox.getPosition(x,y);
ok((x.value==0 & y.value==104),"Preferences pane is panned up","Got "+x.value+" "+y.value+", expected 0,104");
ok((x.value==0 & y.value==distance),"Preferences pane is panned up","Got "+x.value+" "+y.value+", expected 0," + distance);
// Move preferences pane down
dragElement(prefsList,w/2,h/4,w/2,h/2);

Просмотреть файл

@ -67,6 +67,10 @@ let modules = {
home: {
uri: "chrome://browser/content/aboutHome.xhtml",
privileged: true
},
"sync-tabs": {
uri: "chrome://browser/content/aboutTabs.xhtml",
privileged: true
}
}
@ -141,6 +145,12 @@ AboutHome.prototype = {
classID: Components.ID("{b071364f-ab68-4669-a9db-33fca168271a}")
}
function AboutSyncTabs() {}
AboutSyncTabs.prototype = {
__proto__: AboutGeneric.prototype,
classID: Components.ID("{d503134a-f6f3-4824-bc3c-09c123177944}")
}
const components = [AboutFirstrun, AboutFennec, AboutRights,
AboutCertError, AboutFirefox, AboutHome];
AboutCertError, AboutFirefox, AboutHome, AboutSyncTabs];
const NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

Просмотреть файл

@ -40,50 +40,97 @@ const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
function openWindow(aParent, aURL, aTarget, aFeatures) {
let wwatch = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);
return wwatch.openWindow(aParent, aURL, aTarget, aFeatures, null);
}
function resolveURIInternal(aCmdLine, aArgument) {
let uri = aCmdLine.resolveURI(aArgument);
if (!(uri instanceof Ci.nsIFileURL))
return uri;
try {
if (uri.file.exists())
return uri;
}
catch (e) {
Cu.reportError(e);
}
try {
let urifixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
uri = urifixup.createFixupURI(aArgument, 0);
}
catch (e) {
Cu.reportError(e);
}
return uri;
}
function BrowserCLH() { }
BrowserCLH.prototype = {
//
// nsICommandLineHandler
//
handle: function fs_handle(cmdLine) {
handle: function fs_handle(aCmdLine) {
// Instantiate the search service so the search engine cache is created now
// instead when the application is running. The install process will register
// this component by using the -silent command line flag, thereby creating
// the cache during install, not runtime.
// NOTE: This code assumes this CLH is run before the nsDefaultCLH, which
// consumes the "-silent" flag.
if (cmdLine.findFlag("silent", false) > -1) {
if (aCmdLine.findFlag("silent", false) > -1) {
let searchService = Cc["@mozilla.org/browser/search-service;1"].
getService(Ci.nsIBrowserSearchService);
let autoComplete = Cc["@mozilla.org/autocomplete/search;1?name=history"].
getService(Ci.nsIAutoCompleteSearch);
}
// Handle chrome windows loaded via commandline
let chromeParam = aCmdLine.handleFlagWithParam("chrome", false);
if (chromeParam) {
try {
// only load URIs which do not inherit chrome privs
let features = "chrome,dialog=no,all";
let uri = resolveURIInternal(aCmdLine, chromeParam);
let netutil = Cc["@mozilla.org/network/util;1"].getService(Ci.nsINetUtil);
if (!netutil.URIChainHasFlags(uri, Ci.nsIHttpProtocolHandler.URI_INHERITS_SECURITY_CONTEXT)) {
openWindow(null, uri.spec, "_blank", features);
aCmdLine.preventDefault = true;
}
}
catch (e) {
Cu.reportError(e);
}
}
let win;
try {
var windowMediator =
Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
win = windowMediator.getMostRecentWindow("navigator:browser");
if (!win)
return;
win.focus();
cmdLine.preventDefault = true;
aCmdLine.preventDefault = true;
} catch (e) { }
// Assumption: All CLH arguments we've received have been sent remotely,
// or we wouldn't already have a window. Therefore: open 'em all!
for (let i = 0; i < cmdLine.length; i++) {
let arg = cmdLine.getArgument(i);
for (let i = 0; i < aCmdLine.length; i++) {
let arg = aCmdLine.getArgument(i);
if (!arg || arg[0] == '-')
continue;
let uri = cmdLine.resolveURI(arg);
let uri = resolveURIInternal(aCmdLine, arg);
if (uri)
win.browserDOMWindow.openURI(uri, null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB, null);
}

Просмотреть файл

@ -68,8 +68,7 @@ EXTRA_COMPONENTS = \
AddonUpdateService.js \
$(NULL)
DIRS = protocols \
phone \
DIRS = phone \
$(NULL)
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -11,6 +11,8 @@ component {972efe64-8ac0-4e91-bdb0-22835d987815} AboutRedirector.js
contract @mozilla.org/network/protocol/about;1?what=certerror {972efe64-8ac0-4e91-bdb0-22835d987815}
component {b071364f-ab68-4669-a9db-33fca168271a} AboutRedirector.js
contract @mozilla.org/network/protocol/about;1?what=home {b071364f-ab68-4669-a9db-33fca168271a}
component {d503134a-f6f3-4824-bc3c-09c123177944} AboutRedirector.js
contract @mozilla.org/network/protocol/about;1?what=sync-tabs {d503134a-f6f3-4824-bc3c-09c123177944}
# DirectoryProvider.js
component {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} DirectoryProvider.js

Просмотреть файл

@ -1,54 +0,0 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla.
#
# The Initial Developer of the Original Code is
# the Mozilla Foundation <http://www.mozilla.org/>.
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Brad Lassey <blassey@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = protocols
EXTRA_COMPONENTS = nsTelProtocolHandler.manifest
EXTRA_PP_COMPONENTS = nsTelProtocolHandler.js
ifdef WINCE
DEFINES += -DWINCE=$(WINCE)
endif
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -1,151 +0,0 @@
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
const kSCHEME = "tel";
// Mozilla defined
const kSIMPLEURI_CONTRACTID = "@mozilla.org/network/simple-uri;1";
const kIOSERVICE_CONTRACTID = "@mozilla.org/network/io-service;1";
const Ci = Components.interfaces;
function TelProtocol()
{
}
TelProtocol.prototype =
{
classID: Components.ID("d4bc06cc-fa9f-48ce-98e4-5326ca96ba28"),
contractID: kPROTOCOL_CONTRACTID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),
scheme: kSCHEME,
defaultPort: -1,
protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE |
Ci.nsIProtocolHandler.URI_NOAUTH,
allowPort: function(port, scheme)
{
return false;
},
newURI: function(spec, charset, baseURI)
{
var uri = Components.classes[kSIMPLEURI_CONTRACTID].createInstance(Ci.nsIURI);
uri.spec = spec;
return uri;
},
newChannel: function(aURI)
{
// aURI is a nsIUri, so get a string from it using .spec
let phoneNumber = aURI.spec;
// strip away the kSCHEME: part
phoneNumber = phoneNumber.substring(phoneNumber.indexOf(":") + 1, phoneNumber.length);
phoneNumber = encodeURI(phoneNumber);
#ifdef WINCE
try {
//try tel:
let channel = new nsWinceTelChannel (aURI, phoneNumber);
return channel;
} catch(e){
//tel not registered, try the next one
}
#endif
var ios = Components.classes[kIOSERVICE_CONTRACTID].getService(Ci.nsIIOService);
try {
//try voipto:
let channel = ios.newChannel("voipto:"+phoneNumber, null, null);
return channel;
} catch(e){
//voipto not registered, try the next one
}
try {
//try callto:
let channel = ios.newChannel("callto:"+phoneNumber, null, null);
return channel;
} catch(e){
//callto not registered, try the next one
}
//try wtai:
//this is our last equivalent protocol, so if it fails pass the exception on
let channel = ios.newChannel("wtai:"+phoneNumber, null, null);
return channel;
}
};
var components = [TelProtocol];
const NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
#ifdef WINCE
function nsWinceTelChannel(URI, phoneNumber)
{
this.URI = URI;
this.originalURI = URI;
this.phoneNumber = phoneNumber
}
nsWinceTelChannel.prototype.QueryInterface =
function bc_QueryInterface(iid)
{
if (!iid.equals(Ci.nsIChannel) && !iid.equals(Ci.nsIRequest) &&
!iid.equals(Ci.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
/* nsIChannel */
nsWinceTelChannel.prototype.loadAttributes = null;
nsWinceTelChannel.prototype.contentLength = 0;
nsWinceTelChannel.prototype.owner = null;
nsWinceTelChannel.prototype.loadGroup = null;
nsWinceTelChannel.prototype.notificationCallbacks = null;
nsWinceTelChannel.prototype.securityInfo = null;
nsWinceTelChannel.prototype.open =
nsWinceTelChannel.prototype.asyncOpen =
function bc_open(observer, ctxt)
{
var phoneInterface= Components.classes["@mozilla.org/phone/support;1"].createInstance(Ci.nsIPhoneSupport);
phoneInterface.makeCall(this.phoneNumber,"",false);
// We don't throw this (a number, not a real 'resultcode') because it
// upsets xpconnect if we do (error in the js console).
Components.returnCode = NS_ERROR_NO_CONTENT;
}
nsWinceTelChannel.prototype.asyncRead =
function bc_asyncRead(listener, ctxt)
{
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}
/* nsIRequest */
nsWinceTelChannel.prototype.isPending =
function bc_isPending()
{
return true;
}
nsWinceTelChannel.prototype.status = Components.results.NS_OK;
nsWinceTelChannel.prototype.cancel =
function bc_cancel(status)
{
this.status = status;
}
nsWinceTelChannel.prototype.suspend =
nsWinceTelChannel.prototype.resume =
function bc_suspres()
{
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}
#endif

Просмотреть файл

@ -1,2 +0,0 @@
component {d4bc06cc-fa9f-48ce-98e4-5326ca96ba28} nsTelProtocolHandler.js
contract @mozilla.org/network/protocol;1?name=tel {d4bc06cc-fa9f-48ce-98e4-5326ca96ba28}

Просмотреть файл

@ -52,6 +52,7 @@ esac
MOZ_XUL_APP=1
MOZ_ENABLE_LIBXUL=1
MOZ_SERVICES_SYNC=1
MOZ_NO_XPCOM_OBSOLETE=1
if test "$LIBXUL_SDK"; then
MOZ_XULRUNNER=1

Просмотреть файл

@ -0,0 +1 @@
<!ENTITY remoteTabs.title "Tabs from my other computers">

Просмотреть файл

@ -18,3 +18,11 @@
<!ENTITY homepage.none "Blank Page">
<!ENTITY homepage.default "&brandShortName; Start">
<!ENTITY homepage.currentpage "Use Current Page">
<!ENTITY sync.title "Sync">
<!ENTITY sync.username "Username">
<!ENTITY sync.password "Password">
<!ENTITY sync.secretPhrase "Secret Phrase">
<!ENTITY sync.deviceName "Device Name">
<!ENTITY sync.connect "Connect">
<!ENTITY sync.disconnect "Disconnect">
<!ENTITY sync.syncNow "Sync Now">

Просмотреть файл

@ -5,6 +5,7 @@
locale/@AB_CD@/browser/about.dtd (%chrome/about.dtd)
locale/@AB_CD@/browser/aboutCertError.dtd (%chrome/aboutCertError.dtd)
locale/@AB_CD@/browser/aboutHome.dtd (%chrome/aboutHome.dtd)
locale/@AB_CD@/browser/aboutTabs.dtd (%chrome/aboutTabs.dtd)
locale/@AB_CD@/browser/browser.dtd (%chrome/browser.dtd)
locale/@AB_CD@/browser/browser.properties (%chrome/browser.properties)
locale/@AB_CD@/browser/config.dtd (%chrome/config.dtd)

Просмотреть файл

@ -47,13 +47,11 @@ mobile/chrome/Makefile
mobile/chrome/tests/Makefile
mobile/components/Makefile
mobile/components/phone/Makefile
mobile/components/protocols/Makefile
mobile/installer/Makefile
mobile/locales/Makefile
mobile/Makefile
mobile/themes/hildon/Makefile
mobile/themes/Makefile
mobile/themes/wince/Makefile"
mobile/themes/core/Makefile"
if test -n "$MOZ_UPDATE_PACKAGING"; then
add_makefiles "

Просмотреть файл

@ -1,5 +1,4 @@
# ***** BEGIN LICENSE BLOCK *****
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
@ -46,15 +45,8 @@ include $(DEPTH)/config/autoconf.mk
#
# Theme Selection
#
# Maemo hildon
# Windows Mobile wince
#
ifdef WINCE
DIRS = wince
else
# default to hildon for desktop builds too
DIRS = hildon
endif
# Add the core theme files
DIRS = core
include $(topsrcdir)/config/rules.mk

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,50 @@
body {
color: black;
font-family: "Nokia Sans", Tahoma, Arial, Helvetica, sans-serif;
margin: 0;
padding: 0;
}
h1 {
font-size: 24px;
text-align: center;
}
h2 {
background-color: lightgray;
font-size: 24px;
font-weight: bold;
margin: 0;
padding: 8px;
}
.tab {
border-bottom: solid 1px rgb(207, 207, 207);
min-height: 70px;
padding-right: 32px;
position: relative;
}
.tab[selected] {
background-color: #8db8d8;
}
.icon {
height: 32px;
left: 16px;
position: absolute;
top: 4px;
width: 32px;
}
.info {
margin-left: 32px;
overflow: hidden;
padding-left: 32px;
white-space: nowrap;
}
.title {
font-size: 24px;
margin-top: 4px;
}
.url {
color: blue;
font-size: 18px;
margin-top: 4px;
}

Просмотреть файл

@ -799,6 +799,18 @@ box[type="documenttab"]:only-child .documenttab-close {
list-style-image: url("images/newtab-active-64.png");
}
#remotetabs-button {
list-style-image: url("images/remotetabs-default-64.png");
}
#remotetabs-button:not([disabled]):hover:active {
list-style-image: url("images/remotetabs-active-64.png");
}
#remotetabs-button[disabled] {
list-style-image: url("images/remotetabs-disabled-64.png");
}
/* bookmark editor ------------------------------------------------------- */
#bookmark-container {
padding: 8px; /* core spacing */

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 31 KiB

После

Ширина:  |  Высота:  |  Размер: 31 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 2.6 KiB

После

Ширина:  |  Высота:  |  Размер: 2.6 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.0 KiB

После

Ширина:  |  Высота:  |  Размер: 3.0 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 2.7 KiB

После

Ширина:  |  Высота:  |  Размер: 2.7 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 15 KiB

После

Ширина:  |  Высота:  |  Размер: 15 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 416 B

После

Ширина:  |  Высота:  |  Размер: 416 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 416 B

После

Ширина:  |  Высота:  |  Размер: 416 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 246 B

После

Ширина:  |  Высота:  |  Размер: 246 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 286 B

После

Ширина:  |  Высота:  |  Размер: 286 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 280 B

После

Ширина:  |  Высота:  |  Размер: 280 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 210 B

После

Ширина:  |  Высота:  |  Размер: 210 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 235 B

После

Ширина:  |  Высота:  |  Размер: 235 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 206 B

После

Ширина:  |  Высота:  |  Размер: 206 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 242 B

После

Ширина:  |  Высота:  |  Размер: 242 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 301 B

После

Ширина:  |  Высота:  |  Размер: 301 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.0 KiB

После

Ширина:  |  Высота:  |  Размер: 3.0 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.2 KiB

После

Ширина:  |  Высота:  |  Размер: 3.2 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.3 KiB

После

Ширина:  |  Высота:  |  Размер: 3.3 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.1 KiB

После

Ширина:  |  Высота:  |  Размер: 3.1 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 2.8 KiB

После

Ширина:  |  Высота:  |  Размер: 2.8 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.4 KiB

После

Ширина:  |  Высота:  |  Размер: 3.4 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.1 KiB

После

Ширина:  |  Высота:  |  Размер: 3.1 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 1.1 KiB

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 570 B

После

Ширина:  |  Высота:  |  Размер: 570 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 388 B

После

Ширина:  |  Высота:  |  Размер: 388 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 782 B

После

Ширина:  |  Высота:  |  Размер: 782 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 635 B

После

Ширина:  |  Высота:  |  Размер: 635 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 447 B

После

Ширина:  |  Высота:  |  Размер: 447 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 839 B

После

Ширина:  |  Высота:  |  Размер: 839 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 675 B

После

Ширина:  |  Высота:  |  Размер: 675 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 1.9 KiB

После

Ширина:  |  Высота:  |  Размер: 1.9 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 2.0 KiB

После

Ширина:  |  Высота:  |  Размер: 2.0 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.2 KiB

После

Ширина:  |  Высота:  |  Размер: 3.2 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 2.7 KiB

После

Ширина:  |  Высота:  |  Размер: 2.7 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 2.5 KiB

После

Ширина:  |  Высота:  |  Размер: 2.5 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 2.0 KiB

После

Ширина:  |  Высота:  |  Размер: 2.0 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 427 B

После

Ширина:  |  Высота:  |  Размер: 427 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 430 B

После

Ширина:  |  Высота:  |  Размер: 430 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 345 B

После

Ширина:  |  Высота:  |  Размер: 345 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 457 B

После

Ширина:  |  Высота:  |  Размер: 457 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.0 KiB

После

Ширина:  |  Высота:  |  Размер: 3.0 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.2 KiB

После

Ширина:  |  Высота:  |  Размер: 3.2 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.3 KiB

После

Ширина:  |  Высота:  |  Размер: 3.3 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 960 B

После

Ширина:  |  Высота:  |  Размер: 960 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 699 B

После

Ширина:  |  Высота:  |  Размер: 699 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.5 KiB

После

Ширина:  |  Высота:  |  Размер: 3.5 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 5.4 KiB

После

Ширина:  |  Высота:  |  Размер: 5.4 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 5.3 KiB

После

Ширина:  |  Высота:  |  Размер: 5.3 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 1.4 KiB

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 854 B

После

Ширина:  |  Высота:  |  Размер: 854 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 2.9 KiB

После

Ширина:  |  Высота:  |  Размер: 2.9 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 2.8 KiB

После

Ширина:  |  Высота:  |  Размер: 2.8 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.0 KiB

После

Ширина:  |  Высота:  |  Размер: 3.0 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 2.7 KiB

После

Ширина:  |  Высота:  |  Размер: 2.7 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 876 B

После

Ширина:  |  Высота:  |  Размер: 876 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 4.8 KiB

После

Ширина:  |  Высота:  |  Размер: 4.8 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 573 B

После

Ширина:  |  Высота:  |  Размер: 573 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 1.9 KiB

После

Ширина:  |  Высота:  |  Размер: 1.9 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 1.7 KiB

После

Ширина:  |  Высота:  |  Размер: 1.7 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 123 B

После

Ширина:  |  Высота:  |  Размер: 123 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 2.8 KiB

После

Ширина:  |  Высота:  |  Размер: 2.8 KiB

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше