Merge fx-team to m-c a=merge

This commit is contained in:
Wes Kocher 2014-09-16 15:59:12 -07:00
commit 214c0e0ea2
76 changed files with 48677 additions and 142 deletions

View File

@ -81,3 +81,15 @@ GPATH
# Git clone directory for updating web-platform-tests # Git clone directory for updating web-platform-tests
^testing/web-platform/sync/ ^testing/web-platform/sync/
# Loop web client build/deploy dependencies
^browser/components/loop/standalone/bower_components
# Loop legal content build/deploy artifacts
# XXX Once a grunt contrib-clean command has been added (bug 1066491), or
# once legal has centralized their ToS and PP hosting infrastructure,
# (expected Q4 2014) the legal doc build stuff for Loop can be removed,
# including the following three lines
^browser/components/loop/standalone/content/legal/styles/.*\.css$
^browser/components/loop/standalone/content/legal/terms/en_US\.html$

View File

@ -132,7 +132,4 @@ function closeConnection() {
return deferred.promise; return deferred.promise;
} }
// bug 1042976 - temporary test disable
module.exports = {};
require('sdk/test/runner').runTestsFromModule(module); require('sdk/test/runner').runTestsFromModule(module);

View File

@ -292,7 +292,7 @@ pref("browser.slowStartup.maxSamples", 5);
// This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into // This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into
// this page over http opens us up to a man-in-the-middle attack that we'd rather not face. If you are a downstream // this page over http opens us up to a man-in-the-middle attack that we'd rather not face. If you are a downstream
// repackager of this code using an alternate snippet url, please keep your users safe // repackager of this code using an alternate snippet url, please keep your users safe
pref("browser.aboutHomeSnippets.updateUrl", "https://snippets.mozilla.com/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/"); pref("browser.aboutHomeSnippets.updateUrl", "https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/");
pref("browser.enable_automatic_image_resizing", true); pref("browser.enable_automatic_image_resizing", true);
pref("browser.chrome.site_icons", true); pref("browser.chrome.site_icons", true);
@ -1598,7 +1598,7 @@ pref("loop.throttled", false);
pref("loop.enabled", true); pref("loop.enabled", true);
pref("loop.throttled", true); pref("loop.throttled", true);
pref("loop.soft_start_ticket_number", -1); pref("loop.soft_start_ticket_number", -1);
pref("loop.soft_start_hostname", "soft-start.loop-dev.stage.mozaws.net"); pref("loop.soft_start_hostname", "soft-start.loop.services.mozilla.com");
#endif #endif
pref("loop.server", "https://loop.services.mozilla.com"); pref("loop.server", "https://loop.services.mozilla.com");

View File

@ -189,12 +189,12 @@ let gGestureSupport = {
let isVerticalSwipe = false; let isVerticalSwipe = false;
if (aEvent.direction == aEvent.DIRECTION_UP) { if (aEvent.direction == aEvent.DIRECTION_UP) {
if (content.pageYOffset > 0) { if (gMultiProcessBrowser || content.pageYOffset > 0) {
return false; return false;
} }
isVerticalSwipe = true; isVerticalSwipe = true;
} else if (aEvent.direction == aEvent.DIRECTION_DOWN) { } else if (aEvent.direction == aEvent.DIRECTION_DOWN) {
if (content.pageYOffset < content.scrollMaxY) { if (gMultiProcessBrowser || content.pageYOffset < content.scrollMaxY) {
return false; return false;
} }
isVerticalSwipe = true; isVerticalSwipe = true;

View File

@ -393,7 +393,8 @@ panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .a
visibility: visible; visibility: visible;
} }
#urlbar:not([actiontype]) > #urlbar-display-box { #urlbar:not([actiontype]) > #urlbar-display-box,
#urlbar:not([actiontype="switchtab"]) > #urlbar-display-box > .urlbar-display-switchtab {
display: none; display: none;
} }

View File

@ -756,7 +756,7 @@
</hbox> </hbox>
</box> </box>
<box id="urlbar-display-box" align="center"> <box id="urlbar-display-box" align="center">
<label id="urlbar-display" value="&urlbar.switchToTab.label;"/> <label class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/>
</box> </box>
<hbox id="urlbar-icons"> <hbox id="urlbar-icons">
<image id="page-report-button" <image id="page-report-button"

View File

@ -65,7 +65,8 @@ add_task(function*() {
let tab = gBrowser.addTab("about:about"); let tab = gBrowser.addTab("about:about");
yield promiseTabLoaded(tab); yield promiseTabLoaded(tab);
yield check_a11y_label("% about", "about:about moz-action:switchtab,about:about Tab"); let actionURL = makeActionURI("switchtab", {url: "about:about"}).spec;
yield check_a11y_label("% about", "about:about " + actionURL + " Tab");
yield promisePopupHidden(gURLBar.popup); yield promisePopupHidden(gURLBar.popup);
gBrowser.removeTab(tab); gBrowser.removeTab(tab);

View File

@ -34,7 +34,7 @@ function test() {
"The test tab doesn't have the busy attribute"); "The test tab doesn't have the busy attribute");
// Set the urlbar to include the moz-action // Set the urlbar to include the moz-action
gURLBar.value = "moz-action:switchtab," + testURL; gURLBar.value = "moz-action:switchtab," + JSON.stringify({url: testURL});
// Focus the urlbar so we can press enter // Focus the urlbar so we can press enter
gURLBar.focus(); gURLBar.focus();

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let testURL = "http://example.org/browser/browser/base/content/test/general/dummy_page.html"; let testURL = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
let testActionURL = "moz-action:switchtab," + testURL; let testActionURL = "moz-action:switchtab," + JSON.stringify({url: testURL});
testURL = gURLBar.trimValue(testURL); testURL = gURLBar.trimValue(testURL);
let testTab; let testTab;

View File

@ -72,7 +72,7 @@ function test() {
"The test tab doesn't have the busy attribute"); "The test tab doesn't have the busy attribute");
// Set the urlbar to include the moz-action // Set the urlbar to include the moz-action
aDestWindow.gURLBar.value = "moz-action:switchtab," + testURL; aDestWindow.gURLBar.value = "moz-action:switchtab," + JSON.stringify({url: testURL});
// Focus the urlbar so we can press enter // Focus the urlbar so we can press enter
aDestWindow.gURLBar.focus(); aDestWindow.gURLBar.focus();

View File

@ -666,6 +666,11 @@ function assertWebRTCIndicatorStatus(expected) {
} }
} }
function makeActionURI(action, params) {
let url = "moz-action:" + action + "," + JSON.stringify(params);
return NetUtil.newURI(url);
}
function waitForNewTab(aTabBrowser) { function waitForNewTab(aTabBrowser) {
return promiseWaitForEvent(aTabBrowser.tabContainer, "TabOpen"); return promiseWaitForEvent(aTabBrowser.tabContainer, "TabOpen");
} }

View File

@ -150,7 +150,12 @@
var action = this._parseActionUrl(aValue); var action = this._parseActionUrl(aValue);
if (action) { if (action) {
returnValue = action.param; switch (action.type) {
case "switchtab": {
returnValue = action.params.url;
break;
}
}
} }
// Set the actiontype only if the user is not overriding actions. // Set the actiontype only if the user is not overriding actions.
@ -300,9 +305,9 @@
let matchLastLocationChange = true; let matchLastLocationChange = true;
if (action) { if (action) {
url = action.param;
if (this.hasAttribute("actiontype")) { if (this.hasAttribute("actiontype")) {
if (action.type == "switchtab") { if (action.type == "switchtab") {
url = action.params.url;
this.handleRevert(); this.handleRevert();
let prevTab = gBrowser.selectedTab; let prevTab = gBrowser.selectedTab;
if (switchToTabHavingURI(url) && if (switchToTabHavingURI(url) &&
@ -739,9 +744,28 @@
if (!aUrl.startsWith("moz-action:")) if (!aUrl.startsWith("moz-action:"))
return null; return null;
// url is in the format moz-action:ACTION,PARAM // URL is in the format moz-action:ACTION,PARAMS
let [, action, param] = aUrl.match(/^moz-action:([^,]+),(.*)$/); // Where PARAMS is a JSON encoded object.
return {type: action, param: param}; aUrl = decodeURI(aUrl);
let [, type, params] = aUrl.match(/^moz-action:([^,]+),(.*)$/);
let action = {
type: type,
};
try {
action.params = JSON.parse(params);
} catch (e) {
// If this failed, we assume that params is not a JSON object, and
// is instead just a flat string. This will happen when
// UnifiedComplete is disabled - in which case, the param is always
// a URL.
action.params = {
url: params,
}
}
return action;
]]></body> ]]></body>
</method> </method>
@ -910,13 +934,13 @@
var where = whereToOpenLink(aEvent, false, true); var where = whereToOpenLink(aEvent, false, true);
searchBar.doSearch(search, where); searchBar.doSearch(search, where);
} }
]]></body> ]]></body>
</method> </method>
</implementation> </implementation>
</binding> </binding>
<binding id="urlbar-rich-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup"> <binding id="urlbar-rich-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup">
<implementation> <implementation>
<field name="_maxResults">0</field> <field name="_maxResults">0</field>
<field name="_bundle" readonly="true"> <field name="_bundle" readonly="true">
@ -1677,10 +1701,12 @@
sizetopopup="none"> sizetopopup="none">
<xul:menupopup> <xul:menupopup>
<xul:menuitem anonid="trackingContentAction.unblock" <xul:menuitem anonid="trackingContentAction.unblock"
hidden="true" label="&trackingContentBlocked.unblock.label2;" hidden="true" label="&trackingContentBlocked.unblock2.label;"
accesskey="&trackingContentBlocked.unblock2.accesskey;"
oncommand="document.getBindingParent(this).disableTrackingContentProtection();"/> oncommand="document.getBindingParent(this).disableTrackingContentProtection();"/>
<xul:menuitem anonid="trackingContentAction.block" <xul:menuitem anonid="trackingContentAction.block"
hidden="true" label="&trackingContentBlocked.block.label;" hidden="true" label="&trackingContentBlocked.block.label;"
accesskey="&trackingContentBlocked.block.accesskey;"
oncommand="document.getBindingParent(this).enableTrackingContentProtection();"/> oncommand="document.getBindingParent(this).enableTrackingContentProtection();"/>
</xul:menupopup> </xul:menupopup>
</xul:button> </xul:button>

View File

@ -1,11 +1,25 @@
#! /usr/bin/env python #! /usr/bin/env python
import os import os
import re
from distutils import spawn from distutils import spawn
import subprocess import subprocess
from threading import Thread from threading import Thread
import argparse import argparse
def find_react_version(lib_dir):
"Finds the React library version number currently used."
for filename in os.listdir(lib_dir):
match = re.match(r"react-(.*)-prod\.js", filename)
if (match and match.group(1)):
return match.group(1)
print 'Unable to find the current react version used in content.'
print 'Please checked the %s directory.' % lib_dir
exit(1)
SHARED_LIBS_DIR=os.path.join(os.path.dirname(__file__), "content", "shared", "libs")
REACT_VERSION=find_react_version(SHARED_LIBS_DIR)
src_files = [] # files to be compiled src_files = [] # files to be compiled
# search for react-tools install # search for react-tools install
@ -15,6 +29,17 @@ if jsx_path is None:
print 'Please do $ npm install -g react-tools' print 'Please do $ npm install -g react-tools'
exit(1) exit(1)
p = subprocess.Popen([jsx_path, '-V'],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
for line in iter(p.stdout.readline, b''):
info = line.rstrip()
if not info == REACT_VERSION:
print 'You have the wrong version of react-tools installed'
print 'Please use version %s' % REACT_VERSION
exit(1)
# parse the CLI arguments # parse the CLI arguments
description = 'Loop build tool for JSX files. ' + \ description = 'Loop build tool for JSX files. ' + \
'Will scan entire loop directory and compile them in place. ' + \ 'Will scan entire loop directory and compile them in place. ' + \

View File

@ -284,6 +284,8 @@ loop.conversation = (function(OT, mozL10n) {
this._handleSessionError(); this._handleSessionError();
return; return;
}.bind(this)); }.bind(this));
this._websocket.on("progress", this._handleWebSocketProgress, this);
}, },
/** /**
@ -298,6 +300,40 @@ loop.conversation = (function(OT, mozL10n) {
} }
}, },
/**
* Used to receive websocket progress and to determine how to handle
* it if appropraite.
* If we add more cases here, then we should refactor this function.
*
* @param {Object} progressData The progress data from the websocket.
* @param {String} previousState The previous state from the websocket.
*/
_handleWebSocketProgress: function(progressData, previousState) {
// We only care about the terminated state at the moment.
if (progressData.state !== "terminated")
return;
if (progressData.reason === "cancel") {
this._abortIncomingCall();
return;
}
if (progressData.reason === "timeout" &&
(previousState === "init" || previousState === "alerting")) {
this._abortIncomingCall();
}
},
/**
* Silently aborts an incoming call - stops the alerting, and
* closes the websocket.
*/
_abortIncomingCall: function() {
navigator.mozLoop.stopAlerting();
this._websocket.close();
window.close();
},
/** /**
* Accepts an incoming call. * Accepts an incoming call.
*/ */

View File

@ -284,6 +284,8 @@ loop.conversation = (function(OT, mozL10n) {
this._handleSessionError(); this._handleSessionError();
return; return;
}.bind(this)); }.bind(this));
this._websocket.on("progress", this._handleWebSocketProgress, this);
}, },
/** /**
@ -298,6 +300,40 @@ loop.conversation = (function(OT, mozL10n) {
} }
}, },
/**
* Used to receive websocket progress and to determine how to handle
* it if appropraite.
* If we add more cases here, then we should refactor this function.
*
* @param {Object} progressData The progress data from the websocket.
* @param {String} previousState The previous state from the websocket.
*/
_handleWebSocketProgress: function(progressData, previousState) {
// We only care about the terminated state at the moment.
if (progressData.state !== "terminated")
return;
if (progressData.reason === "cancel") {
this._abortIncomingCall();
return;
}
if (progressData.reason === "timeout" &&
(previousState === "init" || previousState === "alerting")) {
this._abortIncomingCall();
}
},
/**
* Silently aborts an incoming call - stops the alerting, and
* closes the websocket.
*/
_abortIncomingCall: function() {
navigator.mozLoop.stopAlerting();
this._websocket.close();
window.close();
},
/** /**
* Accepts an incoming call. * Accepts an incoming call.
*/ */

View File

@ -36,6 +36,8 @@ loop.CallConnectionWebSocket = (function() {
throw new Error("No websocketToken in options"); throw new Error("No websocketToken in options");
} }
this._lastServerState = "init";
// Set loop.debug.sdk to true in the browser, or standalone: // Set loop.debug.sdk to true in the browser, or standalone:
// localStorage.setItem("debug.websocket", true); // localStorage.setItem("debug.websocket", true);
this._debugWebSocket = this._debugWebSocket =
@ -78,6 +80,16 @@ loop.CallConnectionWebSocket = (function() {
return promise; return promise;
}, },
/**
* Closes the websocket. This shouldn't be the normal action as the server
* will normally close the socket. Only in bad error cases, or where we need
* to close the socket just before closing the window (to avoid an error)
* should we call this.
*/
close: function() {
this.socket.close();
},
_clearConnectionFlags: function() { _clearConnectionFlags: function() {
clearTimeout(this.connectDetails.timeout); clearTimeout(this.connectDetails.timeout);
delete this.connectDetails; delete this.connectDetails;
@ -210,6 +222,7 @@ loop.CallConnectionWebSocket = (function() {
this._log("WS Receiving", event.data); this._log("WS Receiving", event.data);
var previousState = this._lastServerState;
this._lastServerState = msg.state; this._lastServerState = msg.state;
switch(msg.messageType) { switch(msg.messageType) {
@ -218,7 +231,7 @@ loop.CallConnectionWebSocket = (function() {
break; break;
case "progress": case "progress":
this.trigger("progress:" + msg.state); this.trigger("progress:" + msg.state);
this.trigger("progress", msg); this.trigger("progress", msg, previousState);
break; break;
} }
}, },

View File

@ -1,5 +1,14 @@
.module-cache .module-cache
node_modules node_modules
bower_components
*.pyc *.pyc
content/config.js content/config.js
content/VERSION.txt content/VERSION.txt
# XXX Once a grunt contrib-clean command has been added (bug 1066491), or
# once legal has centralized their ToS and PP hosting infrastructure,
# (expected Q4 2014) the legal doc build stuff for Loop can be removed,
# including the following three lines:
content/legal/styles/*.css
content/legal/terms/*.html
content/legal/terms/!index.html

View File

@ -0,0 +1,21 @@
#!/usr/bin/env node
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
module.exports = function (grunt) {
'use strict';
// show elapsed time at the end
require('time-grunt')(grunt);
// load all grunt tasks
require('load-grunt-tasks')(grunt, {scope: 'devDependencies'});
grunt.initConfig({
});
grunt.loadTasks('grunttasks');
}
;

View File

@ -2,15 +2,31 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
# Note that this Makefile is not invoked by the Mozilla build
# system, which is why it can have dependencies on things the
# build system at-large doesn't yet support.
# XXX In the interest of making the build logic simpler and
# more maintainable, we should be trying to implement new
# functionality in Gruntfile.js rather than here.
# Bug 1066176 tracks moving all functionality currently here
# to the Gruntfile and getting rid of this Makefile entirely.
LOOP_SERVER_URL := $(shell echo $${LOOP_SERVER_URL-http://localhost:5000}) LOOP_SERVER_URL := $(shell echo $${LOOP_SERVER_URL-http://localhost:5000})
NODE_LOCAL_BIN=./node_modules/.bin NODE_LOCAL_BIN=./node_modules/.bin
install: install: npm_install tos
npm_install:
@npm install @npm install
test: test:
@echo "Not implemented yet." @echo "Not implemented yet."
tos:
@$(NODE_LOCAL_BIN)/grunt replace marked
@$(NODE_LOCAL_BIN)/grunt sass
lint: lint:
@$(NODE_LOCAL_BIN)/jshint *.js content test @$(NODE_LOCAL_BIN)/jshint *.js content test

View File

@ -9,8 +9,13 @@ NodeJS and npm installed.
Installation Installation
------------ ------------
Fetch and install/build any NPM and bower dependencies, as well as the
localized Terms-of-Service content:
$ make install $ make install
Some of the above is driven by Gruntfile.js.
Configuration Configuration
------------- -------------

View File

@ -0,0 +1,7 @@
{
"name": "loop-client",
"dependencies": {
"tos-pp": "https://github.com/mozilla/legal-docs.git"
},
"devDependencies": {}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 886 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 898 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// Set the current translation content
function setBody(data) {
$("#legal-copy").html(data);
}
function normalizeLocale(lang) {
return lang.replace(/-/g, "_");
}
$(document).ready(function() {
// Get the favorite language
var lang, defaultLang = "en-US";
$.get(loop.config.serverUrl, function(data) {
if (data.hasOwnProperty("i18n")) {
lang = normalizeLocale(data.i18n.lang);
defaultLang = normalizeLocale(data.i18n.defaultLang);
}
if (lang === undefined) {
lang = normalizeLocale(defaultLang);
}
$.get(lang + ".html")
.done(setBody)
.fail(function() {
$.get(defaultLang + ".html")
.done(setBody);
});
});
});

View File

@ -0,0 +1,235 @@
@font-face { font-family: 'Clear Sans'; font-style: normal; font-weight: 400; src: local(Clear Sans), local(ClearSans), url(/legal/fonts/latin/clearsans-regular.eot?#iefix) format(embedded-opentype), url(/legal/fonts/latin/clearsans-regular.woff) format(woff), url(/legal/fonts/latin/clearsans-regular.ttf) format(truetype), url(/legal/fonts/latin/clearsans-regular.svg#clearsans-regular) format(svg); }
@font-face { font-family: 'Fira Sans'; font-style: normal; font-weight: 400; src: local(Fira Sans), local(FiraSans-Regular), url(/legal/fonts/latin/firasans-regular.eot?#iefix) format(embedded-opentype), url(/legal/fonts/latin/firasans-regular.woff) format(woff), url(/legal/fonts/latin/firasans-regular.ttf) format(truetype), url(/legal/fonts/latin/firasans-regular.svg#firasans-regular) format(svg); }
@font-face { font-family: 'Fira Sans'; font-style: normal; font-weight: 300; src: local(Fira Sans Light), local(FiraSans-Light), url(/legal/fonts/latin/firasans-light.eot?#iefix) format(embedded-opentype), url(/legal/fonts/latin/firasans-light.woff) format(woff), url(/legal/fonts/latin/firasans-light.ttf) format(truetype), url(/legal/fonts/latin/firasans-light.svg#firasans-light) format(svg); }
@media (min-width: 321px) and (max-width: 480px) {
#about-mozilla {
display: none;
}
#main-content {
-webkit-box-shadow: rgb(138, 155, 168) 0px 1px 5px;
border-radius: 2px 2px;
box-shadow: rgb(138, 155, 168) 0px 1px 5px;
margin-top: 0px;
max-width: 360px;
min-height: 300px;
min-width: 300px;
padding-bottom: 10px;
padding-left: 20px;
padding-right: 20px;
padding-top: 45px;
position: relative;
top: -28px;
width: 94%;
}
.static {
-webkit-background-size: 48px 51px;
background-position-x: 50%;
background-position-y: 0%;
background-repeat: no-repeat;
background-size: 48px 51px;
height: 51px;
margin-top: 10px;
top: 0px;
width: 100%;
}
body {
padding-bottom: 20px;
}
h1 {
font-size: 20px;
margin-bottom: 20px;
}
html {
background-color: rgb(242, 242, 242);
}
}
@media (min-width: 481px) {
#main-content {
-webkit-box-shadow: rgb(138, 155, 168) 0px 1px 5px;
border-radius: 4px 4px;
box-shadow: rgb(138, 155, 168) 0px 1px 5px;
margin-top: -5px;
min-height: 450px;
padding-bottom: 40px;
padding-left: 40px;
padding-right: 40px;
padding-top: 60px;
width: 420px;
}
.static {
-webkit-background-size: 80px 85px;
background-size: 80px 85px;
height: 85px;
margin-top: 0px;
top: 40px;
width: 80px;
}
body {
padding-bottom: 20px;
}
h1 {
font-size: 24px;
margin-bottom: 32px;
}
html {
background-color: rgb(242, 242, 242);
}
}
@media (max-width: 319px) {
#about-mozilla {
display: none;
}
#main-content {
-webkit-box-shadow: none;
background-position-x: 0px;
background-position-y: 0px;
border-radius: 2px 2px;
box-shadow: none;
margin-top: 10px;
max-width: 360px;
min-height: 0px;
min-width: 250px;
padding: 0px;
position: relative;
top: 0px;
width: 250px;
}
.static {
-webkit-background-size: 48px 51px;
background-position-x: 50%;
background-position-y: 0%;
background-repeat: no-repeat;
background-size: 48px 51px;
display: none;
height: 51px;
margin-top: 10px;
top: 0px;
width: 100%;
}
body {
margin: 0px;
padding: 0px;
}
h1 {
font-size: 18px;
margin-bottom: 10px;
}
html {
background-color: rgb(255, 255, 255);
}
}
#about-mozilla {
-webkit-background-size: 69px 19px;
-webkit-transition-duration: 150ms;
-webkit-transition-property: opacity;
background-image: url(/legal/images/mozilla.png);
background-size: 69px 19px;
height: 19px;
opacity: 0.5;
position: absolute;
right: 12px;
top: 12px;
width: 69px;
}
#main-content {
background-color: rgb(255, 255, 255);
margin-bottom: 0px;
margin-left: auto;
margin-right: auto;
text-align: center;
}
* {
box-sizing: border-box;
}
.static {
background-image: url(/legal/images/firefox.png);
margin-bottom: 0px;
margin-left: auto;
margin-right: auto;
opacity: 1;
background-size: 102px 96px;
height: 102px;
width: 96px;
position: relative;
position: relative;
z-index: 999;
}
a {
color: rgb(0, 149, 221);
cursor: pointer;
}
article {
text-align: left;
}
body {
color: rgb(66, 79, 89);
font-family: 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 14px;
}
h1, h2 {
font-family: 'Fira Sans', Helvetica, Arial, sans-serif;
font-weight: 200;
line-height: 1em;
margin-left: 0px;
margin-right: 0px;
margin-top: 0px;
}
h3 {
font-family: 'Fira Sans', Helvetica, Arial, sans-serif;
font-size: 12px;
line-height: 1em;
margin: 0px;
padding-bottom: 10px;
padding-left: 0px;
padding-right: 0px;
padding-top: 5px;
}
html {
height: 100%;
}
ol {
margin-left: 0px;
padding-left: 20px;
}
p {
font-size: 14px;
}
ul {
margin-left: 0px;
padding-left: 20px;
}

View File

@ -0,0 +1,41 @@
<!doctype html>
<!--[if lt IE 7]> <html class="no-js lt-ie10 lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie10 lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="lt-ie10 lt-ie9"> <![endif]-->
<!--[if IE 9]> <html class="lt-ie10"> <![endif]-->
<!--[if gt IE 9]><!--> <html dir="ltr" lang="en-US"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>WebRTC: Terms of Service</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="/legal/styles/main.css">
</head>
<body>
<div id="fox-logo" class="static"></div>
<div id="stage">
<div id="main-content">
<!--[if lt IE 10]>
<p class="error browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]-->
<header id="legal-header">
<h3>WebRTC</h3>
<h1 id="webrtc-tos-header">Terms of Service</h1>
</header>
<section>
<article id="legal-copy">
Loading...
</article>
</section>
</div>
</div>
<a id="about-mozilla" rel="author" target="_blank" href="https://www.mozilla.org/about/?utm_source=firefox-webrtc&amp;utm_medium=Referral"></a>
<script type="text/javascript" src="/shared/libs/jquery-2.1.0.js"></script>
<script type="text/javascript" src="/config.js"></script>
<script type="text/javascript" src="/legal/js/loader.js"></script>
</body>
</html>

View File

@ -0,0 +1,36 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var path = require('path');
var i18n = require('i18n-abide');
module.exports = function (grunt) {
'use strict';
// convert localized TOS agreement from markdown to html partials.
function rename(destPath, srcFile) {
// Normalize the filenames to use the locale name.
var lang = srcFile.replace('.md', '');
return path.join(destPath, i18n.localeFrom(lang) + '.html');
}
grunt.config('marked', {
options: {
sanitize: false,
gfm: true
},
tos: {
files: [
{
expand: true,
cwd: '<%= project_vars.tos_md_src %>',
src: ['**/*.md'],
dest: '<%= project_vars.tos_html_dest %>',
rename: rename
}
]
}
});
};

View File

@ -0,0 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
module.exports = function (grunt) {
'use strict';
var TEMPLATE_ROOT = 'content/legal';
var TOS_PP_REPO_ROOT = 'bower_components/tos-pp';
grunt.config('project_vars', {
app: "content",
// Translated TOS/PP agreements.
tos_pp_repo_dest: TOS_PP_REPO_ROOT,
tos_md_src: TOS_PP_REPO_ROOT + '/WebRTC_ToS/',
tos_html_dest: TEMPLATE_ROOT + '/terms'
});
};

View File

@ -0,0 +1,37 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
module.exports = function (grunt) {
'use strict';
grunt.config('replace', {
tos: {
src: [
'<%= project_vars.tos_md_src %>/*.md'
],
overwrite: true,
replacements: [{
// remove tags not handle by the markdown-to-HTML conversation
from: /{:\s.*?\s}/g,
to: ''
}, {
// remove unhandled comments
from: /^#\s.*?\n$/m,
to: ''
}, {
// fix "smart quotes"
from: /(“|”)/g,
to: "&quot;"
}, {
// fix "smart quotes"
from: //g,
to: "&#39;"
}, {
from: //g,
to: "&mdash;"
}
]
}
});
};

View File

@ -0,0 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
module.exports = function (grunt) {
'use strict';
grunt.config('sass', {
styles: {
files: {
'<%= project_vars.app %>/legal/styles/main.css':
'<%= project_vars.app %>/legal/styles/main.scss'
}
}
});
};

View File

@ -1,19 +1,33 @@
{ {
"name": "Loop-client", "name": "loop-client",
"description": "Video conferencing app powered by WebRTC.", "description": "Video conferencing app powered by WebRTC",
"version": "0.0.1", "version": "0.0.1",
"repository": {
"type": "git",
"url": "git@github.com:mozilla/loop-client.git"
},
"engines": { "engines": {
"node": "0.10.x", "node": "0.10.x",
"npm":"1.3.x" "npm": "1.3.x"
},
"dependencies": {
"express": "3.x"
}, },
"dependencies": {},
"devDependencies": { "devDependencies": {
"jshint": "2.x" "bower": "1.3.9",
"express": "3.x",
"grunt": "0.4.5",
"grunt-cli": "0.1.13",
"grunt-marked": "0.1.1",
"grunt-sass": "0.14.1",
"grunt-text-replace": "0.3.12",
"i18n-abide": "0.0.22",
"jshint": "2.x",
"load-grunt-tasks": "0.6.0",
"time-grunt": "0.4.0"
}, },
"scripts": { "scripts": {
"test": "make test", "test": "make test",
"start": "make runserver" "start": "make runserver",
} "prepublish": "bower install --config.interactive=false -s"
},
"license": "MPL-2.0"
} }

View File

@ -8,20 +8,29 @@ var app = express();
var port = process.env.PORT || 3000; var port = process.env.PORT || 3000;
var loopServerPort = process.env.LOOP_SERVER_PORT || 5000; var loopServerPort = process.env.LOOP_SERVER_PORT || 5000;
app.get('/content/config.js', function (req, res) { function getConfigFile(req, res) {
"use strict"; "use strict";
res.set('Content-Type', 'text/javascript'); res.set('Content-Type', 'text/javascript');
res.send( res.send(
"var loop = loop || {};" + "var loop = loop || {};" +
"loop.config = loop.config || {};" + "loop.config = loop.config || {};" +
"loop.config.serverUrl = 'http://localhost:" + loopServerPort + "';" "loop.config.serverUrl = 'http://localhost:" + loopServerPort + "';" +
"loop.config.pendingCallTimeout = 20000;"
); );
}
}); app.get('/content/config.js', getConfigFile);
// This lets /test/ be mapped to the right place for running tests // This lets /test/ be mapped to the right place for running tests
app.use('/', express.static(__dirname + '/../')); app.use('/', express.static(__dirname + '/../'));
// Magic so that the legal content works both in the standalone server
// and as static content in the loop-client repo
app.use('/', express.static(__dirname + '/content/'));
app.use('/shared', express.static(__dirname + '/../content/shared/'));
app.get('/config.js', getConfigFile);
// This lets /content/ be mapped right for the static contents. // This lets /content/ be mapped right for the static contents.
app.use('/', express.static(__dirname + '/')); app.use('/', express.static(__dirname + '/'));
// This lets standalone components load images into the UI showcase // This lets standalone components load images into the UI showcase

View File

@ -223,7 +223,8 @@ describe("loop.conversation", function() {
resolve(); resolve();
}); });
return promise; return promise;
} },
on: sinon.spy()
}); });
}); });
@ -268,7 +269,8 @@ describe("loop.conversation", function() {
reject(); reject();
}); });
return promise; return promise;
} },
on: sinon.spy()
}); });
}); });
@ -285,6 +287,102 @@ describe("loop.conversation", function() {
}); });
}); });
}); });
describe("Events", function() {
describe("Call cancelled or timed out before acceptance", function() {
var promise;
beforeEach(function() {
sandbox.stub(loop.CallConnectionWebSocket.prototype, "promiseConnect", function() {
promise = new Promise(function(resolve, reject) {
resolve();
});
return promise;
});
sandbox.stub(loop.CallConnectionWebSocket.prototype, "close");
sandbox.stub(navigator.mozLoop, "stopAlerting");
sandbox.stub(window, "close");
router._setupWebSocketAndCallView();
});
describe("progress - terminated - cancel", function() {
it("should stop alerting", function(done) {
promise.then(function() {
router._websocket.trigger("progress", {
state: "terminated",
reason: "cancel"
});
sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
done();
});
});
it("should close the websocket", function(done) {
promise.then(function() {
router._websocket.trigger("progress", {
state: "terminated",
reason: "cancel"
});
sinon.assert.calledOnce(router._websocket.close);
done();
});
});
it("should close the window", function(done) {
promise.then(function() {
router._websocket.trigger("progress", {
state: "terminated",
reason: "cancel"
});
sinon.assert.calledOnce(window.close);
done();
});
});
});
describe("progress - terminated - timeout (previousState = alerting)", function() {
it("should stop alerting", function(done) {
promise.then(function() {
router._websocket.trigger("progress", {
state: "terminated",
reason: "timeout"
}, "alerting");
sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
done();
});
});
it("should close the websocket", function(done) {
promise.then(function() {
router._websocket.trigger("progress", {
state: "terminated",
reason: "timeout"
}, "alerting");
sinon.assert.calledOnce(router._websocket.close);
done();
});
});
it("should close the window", function(done) {
promise.then(function() {
router._websocket.trigger("progress", {
state: "terminated",
reason: "timeout"
}, "alerting");
sinon.assert.calledOnce(window.close);
done();
});
});
});
});
});
}); });
}); });

View File

@ -99,9 +99,11 @@ class Test1BrowserCall(MarionetteTestCase):
"btn-accept") "btn-accept")
call_button.click() call_button.click()
# expect a video container on standalone side # make sure the standalone progresses to the pending state
video = self.wait_for_element_displayed(By.CLASS_NAME, "media") pending_header = self.wait_for_element_displayed(By.CLASS_NAME,
self.assertEqual(video.tag_name, "div", "expect a video container") "pending-header")
self.assertEqual(pending_header.tag_name, "header",
"expect a pending header")
def accept_and_verify_incoming_call(self): def accept_and_verify_incoming_call(self):
self.marionette.set_context("chrome") self.marionette.set_context("chrome")

View File

@ -61,6 +61,13 @@ add_task(function* test_mozLoop_softStart() {
Services.prefs.setCharPref("loop.soft_start_hostname", SOFT_START_HOSTNAME); Services.prefs.setCharPref("loop.soft_start_hostname", SOFT_START_HOSTNAME);
Services.prefs.setIntPref("loop.soft_start_ticket_number", -1); Services.prefs.setIntPref("loop.soft_start_ticket_number", -1);
registerCleanupFunction(function () {
Services.prefs.clearUserPref("loop.throttled");
Services.prefs.clearUserPref("loop.soft_start");
Services.prefs.clearUserPref("loop.soft_start_hostname");
Services.prefs.clearUserPref("loop.soft_start_ticket_number");
});
let throttled; let throttled;
let ticket; let ticket;

View File

@ -17,6 +17,7 @@ describe("loop.CallConnectionWebSocket", function() {
sandbox.useFakeTimers(); sandbox.useFakeTimers();
dummySocket = { dummySocket = {
close: sinon.spy(),
send: sinon.spy() send: sinon.spy()
}; };
sandbox.stub(window, 'WebSocket').returns(dummySocket); sandbox.stub(window, 'WebSocket').returns(dummySocket);
@ -133,6 +134,16 @@ describe("loop.CallConnectionWebSocket", function() {
}); });
}); });
describe("#close", function() {
it("should close the socket", function() {
callWebSocket.promiseConnect();
callWebSocket.close();
sinon.assert.calledOnce(dummySocket.close);
});
});
describe("#decline", function() { describe("#decline", function() {
it("should send a terminate message to the server", function() { it("should send a terminate message to the server", function() {
callWebSocket.promiseConnect(); callWebSocket.promiseConnect();
@ -212,7 +223,35 @@ describe("loop.CallConnectionWebSocket", function() {
}); });
sinon.assert.called(callWebSocket.trigger); sinon.assert.called(callWebSocket.trigger);
sinon.assert.calledWithExactly(callWebSocket.trigger, "progress", eventData); sinon.assert.calledWithExactly(callWebSocket.trigger, "progress",
eventData, "init");
});
it("should trigger a progress event with the previous state", function() {
var previousEventData = {
messageType: "progress",
state: "alerting"
};
// This first call is to set the previous state of the object
// ready for the main test below.
dummySocket.onmessage({
data: JSON.stringify(previousEventData)
});
var currentEventData = {
messageType: "progress",
state: "terminate",
reason: "reject"
};
dummySocket.onmessage({
data: JSON.stringify(currentEventData)
});
sinon.assert.called(callWebSocket.trigger);
sinon.assert.calledWithExactly(callWebSocket.trigger, "progress",
currentEventData, "alerting");
}); });
it("should trigger a progress:<state> event on the callWebSocket", function() { it("should trigger a progress:<state> event on the callWebSocket", function() {

View File

@ -7,5 +7,6 @@
* @type {Object} * @type {Object}
*/ */
navigator.mozLoop = { navigator.mozLoop = {
getLoopCharPref: function() {} getLoopCharPref: function() {},
getLoopBoolPref: function() {}
}; };

View File

@ -93,7 +93,8 @@ var gMainPane = {
gMainPane.enableE10SChange); gMainPane.enableE10SChange);
let e10sCheckbox = document.getElementById("e10sAutoStart"); let e10sCheckbox = document.getElementById("e10sAutoStart");
let e10sPref = document.getElementById("browser.tabs.remote.autostart"); let e10sPref = document.getElementById("browser.tabs.remote.autostart");
e10sCheckbox.checked = e10sPref.value; let e10sTempPref = document.getElementById("e10sTempPref");
e10sCheckbox.checked = e10sPref.value || e10sTempPref.value;
#endif #endif
// Notify observers that the UI is now ready // Notify observers that the UI is now ready
@ -107,6 +108,19 @@ var gMainPane = {
{ {
let e10sCheckbox = document.getElementById("e10sAutoStart"); let e10sCheckbox = document.getElementById("e10sAutoStart");
let e10sPref = document.getElementById("browser.tabs.remote.autostart"); let e10sPref = document.getElementById("browser.tabs.remote.autostart");
let e10sTempPref = document.getElementById("e10sTempPref");
let prefsToChange;
if (e10sCheckbox.checked) {
// Enabling e10s autostart
prefsToChange = [e10sPref];
} else {
// Disabling e10s autostart
prefsToChange = [e10sPref];
if (e10sTempPref.value) {
prefsToChange.push(e10sTempPref);
}
}
const Cc = Components.classes, Ci = Components.interfaces; const Cc = Components.classes, Ci = Components.interfaces;
let brandName = document.getElementById("bundleBrand").getString("brandShortName"); let brandName = document.getElementById("bundleBrand").getString("brandShortName");
@ -124,13 +138,15 @@ var gMainPane = {
shouldProceed = !cancelQuit.data; shouldProceed = !cancelQuit.data;
if (shouldProceed) { if (shouldProceed) {
e10sPref.value = e10sCheckbox.checked; for (let prefToChange of prefsToChange) {
prefToChange.value = e10sCheckbox.checked;
}
Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart); Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
} }
} }
// Revert the checkbox in case we didn't quit // Revert the checkbox in case we didn't quit
e10sCheckbox.checked = e10sPref.value; e10sCheckbox.checked = e10sPref.value || e10sTempPref.value;
}, },
#endif #endif

View File

@ -13,6 +13,9 @@
<preference id="browser.tabs.remote.autostart" <preference id="browser.tabs.remote.autostart"
name="browser.tabs.remote.autostart" name="browser.tabs.remote.autostart"
type="bool"/> type="bool"/>
<preference id="e10sTempPref"
name="browser.tabs.remote.autostart.1"
type="bool"/>
#endif #endif
<!-- Startup --> <!-- Startup -->

View File

@ -64,7 +64,7 @@ function switchToURL(groupItemOne, groupItemTwo) {
* switch. The tab should be opened in group two and not in group one. * switch. The tab should be opened in group two and not in group one.
*/ */
// Set the urlbar to include the moz-action. // Set the urlbar to include the moz-action.
gURLBar.value = "moz-action:switchtab,about:mozilla"; gURLBar.value = "moz-action:switchtab," + JSON.stringify({url: "about:mozilla"});
// Focus the urlbar so we can press enter. // Focus the urlbar so we can press enter.
gURLBar.focus(); gURLBar.focus();
// Press enter. // Press enter.

View File

@ -67,7 +67,7 @@ function switchToURL(groupItemOne, groupItemTwo) {
* switch. The selected group should be group one. * switch. The selected group should be group one.
*/ */
// Set the urlbar to include the moz-action. // Set the urlbar to include the moz-action.
gURLBar.value = "moz-action:switchtab,about:mozilla"; gURLBar.value = "moz-action:switchtab," + JSON.stringify({url: "about:mozilla"});
// Focus the urlbar so we can press enter. // Focus the urlbar so we can press enter.
gURLBar.focus(); gURLBar.focus();
// Press enter. // Press enter.

View File

@ -403,7 +403,7 @@
<tabpanels flex="1"/> <tabpanels flex="1"/>
</tabbox> </tabbox>
</hbox> </hbox>
<toolbar id="statusbar-line-col" class="chromeclass-toolbar"/> <toolbar id="statusbar-line-col" class="devtools-toolbar"/>
</notificationbox> </notificationbox>
</window> </window>

View File

@ -83,8 +83,7 @@ function CheckLockState() {
certCheckResult.textContent = sUnknown; certCheckResult.textContent = sUnknown;
if (AppManager.connection && if (AppManager.connection &&
AppManager.connection.status == Connection.Status.CONNECTED && AppManager.connection.status == Connection.Status.CONNECTED) {
AppManager.preferenceFront) {
// ADB check // ADB check
if (AppManager.selectedRuntime instanceof USBRuntime) { if (AppManager.selectedRuntime instanceof USBRuntime) {

View File

@ -732,15 +732,17 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY mixedContentBlocked2.unblock.label "Disable protection for now"> <!ENTITY mixedContentBlocked2.unblock.label "Disable protection for now">
<!ENTITY mixedContentBlocked2.unblock.accesskey "D"> <!ENTITY mixedContentBlocked2.unblock.accesskey "D">
<!ENTITY mixedContentBlocked2.block.label "Enable protection"> <!ENTITY mixedContentBlocked2.block.label "Enable protection">
<!ENTITY mixedContentBlocked2.block.accesskey "B"> <!ENTITY mixedContentBlocked2.block.accesskey "E">
<!ENTITY mixedContentBlocked2.disabled.message "Protection is disabled"> <!ENTITY mixedContentBlocked2.disabled.message "Protection is disabled">
<!ENTITY trackingContentBlocked.message "Tracking"> <!ENTITY trackingContentBlocked.message "Tracking">
<!ENTITY trackingContentBlocked.moreinfo "Parts of the page that track your online activity have been blocked."> <!ENTITY trackingContentBlocked.moreinfo "Parts of the page that track your online activity have been blocked.">
<!ENTITY trackingContentBlocked.learnMore "Learn More"> <!ENTITY trackingContentBlocked.learnMore "Learn More">
<!ENTITY trackingContentBlocked.options "Options"> <!ENTITY trackingContentBlocked.options "Options">
<!ENTITY trackingContentBlocked.unblock.label2 "Disable protection for this site"> <!ENTITY trackingContentBlocked.unblock2.label "Disable protection for this site">
<!ENTITY trackingContentBlocked.unblock2.accesskey "D">
<!ENTITY trackingContentBlocked.block.label "Enable protection"> <!ENTITY trackingContentBlocked.block.label "Enable protection">
<!ENTITY trackingContentBlocked.block.accesskey "E">
<!ENTITY trackingContentBlocked.disabled.message "Tracking protection is disabled"> <!ENTITY trackingContentBlocked.disabled.message "Tracking protection is disabled">
<!ENTITY pointerLock.notification.message "Press ESC at any time to show it again."> <!ENTITY pointerLock.notification.message "Press ESC at any time to show it again.">

View File

@ -901,7 +901,7 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
-moz-margin-end: 3px; -moz-margin-end: 3px;
} }
#urlbar-display { .urlbar-display {
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
-moz-margin-start: 0; -moz-margin-start: 0;

View File

@ -2147,7 +2147,7 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
-moz-margin-end: 3px; -moz-margin-end: 3px;
} }
#urlbar-display { .urlbar-display {
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
color: GrayText; color: GrayText;

View File

@ -1247,7 +1247,7 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
-moz-margin-end: 3px; -moz-margin-end: 3px;
} }
#urlbar-display { .urlbar-display {
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
-moz-margin-start: 0; -moz-margin-start: 0;

View File

@ -91,6 +91,19 @@ public class ShareDialog extends LocaleAware.LocaleAwareActivity implements Send
super.onDestroy(); super.onDestroy();
} }
/**
* Show a toast indicating we were started with no URL, and then stop.
*/
private void abortDueToNoURL() {
Log.e(LOGTAG, "Unable to process shared intent. No URL found!");
// Display toast notifying the user of failure (most likely a developer who screwed up
// trying to send a share intent).
Toast toast = Toast.makeText(this, getResources().getText(R.string.overlay_share_no_url), Toast.LENGTH_SHORT);
toast.show();
finish();
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -102,17 +115,14 @@ public class ShareDialog extends LocaleAware.LocaleAwareActivity implements Send
// The URL is usually hiding somewhere in the extra text. Extract it. // The URL is usually hiding somewhere in the extra text. Extract it.
final String extraText = intent.getStringExtra(Intent.EXTRA_TEXT); final String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
if (TextUtils.isEmpty(extraText)) {
abortDueToNoURL();
return;
}
final String pageUrl = new WebURLFinder(extraText).bestWebURL(); final String pageUrl = new WebURLFinder(extraText).bestWebURL();
if (TextUtils.isEmpty(pageUrl)) { if (TextUtils.isEmpty(pageUrl)) {
Log.e(LOGTAG, "Unable to process shared intent. No URL found!"); abortDueToNoURL();
// Display toast notifying the user of failure (most likely a developer who screwed up
// trying to send a share intent).
Toast toast = Toast.makeText(this, resources.getText(R.string.overlay_share_no_url), Toast.LENGTH_SHORT);
toast.show();
finish();
return; return;
} }

View File

@ -72,9 +72,7 @@
</style> </style>
<style name="Widget.Home.HistoryListView"> <style name="Widget.Home.HistoryListView">
<item name="android:paddingLeft">50dp</item> <item name="topDivider">true</item>
<item name="android:paddingRight">100dp</item>
<item name="android:paddingTop">0dp</item>
<item name="android:scrollbarStyle">outsideOverlay</item> <item name="android:scrollbarStyle">outsideOverlay</item>
</style> </style>

View File

@ -24,11 +24,7 @@
</style> </style>
<style name="Widget.BookmarksListView" parent="Widget.HomeListView"> <style name="Widget.BookmarksListView" parent="Widget.HomeListView">
<item name="android:paddingTop">30dp</item>
<item name="android:paddingLeft">120dp</item>
<item name="android:paddingRight">120dp</item>
<item name="android:scrollbarStyle">outsideOverlay</item> <item name="android:scrollbarStyle">outsideOverlay</item>
<item name="topDivider">true</item>
</style> </style>
<style name="Widget.TopSitesGridView" parent="Widget.GridView"> <style name="Widget.TopSitesGridView" parent="Widget.GridView">

View File

@ -62,11 +62,7 @@
</style> </style>
<style name="Widget.BookmarksListView" parent="Widget.HomeListView"> <style name="Widget.BookmarksListView" parent="Widget.HomeListView">
<item name="android:paddingTop">30dp</item>
<item name="android:paddingLeft">32dp</item>
<item name="android:paddingRight">32dp</item>
<item name="android:scrollbarStyle">outsideOverlay</item> <item name="android:scrollbarStyle">outsideOverlay</item>
<item name="topDivider">true</item>
</style> </style>
<style name="Widget.TopSitesGridView" parent="Widget.GridView"> <style name="Widget.TopSitesGridView" parent="Widget.GridView">
@ -78,6 +74,9 @@
</style> </style>
<style name="Widget.TopSitesListView" parent="Widget.BookmarksListView"> <style name="Widget.TopSitesListView" parent="Widget.BookmarksListView">
<item name="android:paddingTop">30dp</item>
<item name="android:paddingLeft">32dp</item>
<item name="android:paddingRight">32dp</item>
<item name="topDivider">false</item> <item name="topDivider">false</item>
</style> </style>

View File

@ -4,6 +4,12 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<resources> <resources>
<style name="Widget.TopSitesListView" parent="Widget.BookmarksListView">
<item name="android:paddingTop">30dp</item>
<item name="android:paddingLeft">120dp</item>
<item name="android:paddingRight">120dp</item>
<item name="topDivider">false</item>
</style>
<style name="Widget.TopSitesGridView" parent="Widget.GridView"> <style name="Widget.TopSitesGridView" parent="Widget.GridView">
<item name="android:paddingLeft">55dp</item> <item name="android:paddingLeft">55dp</item>
@ -14,9 +20,6 @@
</style> </style>
<style name="Widget.Home.HistoryListView"> <style name="Widget.Home.HistoryListView">
<item name="android:paddingLeft">50dp</item>
<item name="android:paddingRight">100dp</item>
<item name="android:paddingTop">30dp</item>
<item name="android:scrollbarStyle">outsideOverlay</item> <item name="android:scrollbarStyle">outsideOverlay</item>
<item name="topDivider">true</item> <item name="topDivider">true</item>
</style> </style>

View File

@ -17,9 +17,6 @@
</style> </style>
<style name="Widget.Home.HistoryListView"> <style name="Widget.Home.HistoryListView">
<item name="android:paddingLeft">32dp</item>
<item name="android:paddingRight">32dp</item>
<item name="android:paddingTop">30dp</item>
<item name="android:scrollbarStyle">outsideOverlay</item> <item name="android:scrollbarStyle">outsideOverlay</item>
<item name="topDivider">true</item> <item name="topDivider">true</item>
</style> </style>
@ -30,4 +27,11 @@
<item name="android:paddingRight">212dp</item> <item name="android:paddingRight">212dp</item>
</style> </style>
<style name="Widget.TopSitesListView" parent="Widget.BookmarksListView">
<item name="android:paddingTop">30dp</item>
<item name="android:paddingLeft">32dp</item>
<item name="android:paddingRight">32dp</item>
<item name="topDivider">false</item>
</style>
</resources> </resources>

View File

@ -63,8 +63,10 @@
</style> </style>
<style name="Widget.Home.HistoryListView"> <style name="Widget.Home.HistoryListView">
<item name="android:paddingLeft">32dp</item> <item name="android:paddingTop">0dip</item>
<item name="android:paddingRight">32dp</item> <item name="android:paddingRight">0dip</item>
<item name="android:paddingLeft">0dip</item>
<item name="topDivider">true</item>
<item name="android:scrollbarStyle">outsideOverlay</item> <item name="android:scrollbarStyle">outsideOverlay</item>
</style> </style>

View File

@ -1,7 +1,7 @@
package org.mozilla.gecko.tests; package org.mozilla.gecko.tests;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.home.HomePager; import org.mozilla.gecko.home.HomePager;
import org.mozilla.gecko.sync.Utils;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentValues; import android.content.ContentValues;
@ -32,11 +32,9 @@ public class testBookmarkFolders extends AboutHomeTest {
// Verify the number of folders displayed in the Desktop Bookmarks folder is correct // Verify the number of folders displayed in the Desktop Bookmarks folder is correct
ListView desktopFolderContent = findListViewWithTag(HomePager.LIST_TAG_BOOKMARKS); ListView desktopFolderContent = findListViewWithTag(HomePager.LIST_TAG_BOOKMARKS);
ListAdapter adapter = desktopFolderContent.getAdapter(); ListAdapter adapter = desktopFolderContent.getAdapter();
if (mDevice.type.equals("tablet")) { // On tablets it's 4 folders and 1 view for top padding
mAsserter.is(adapter.getCount(), 5, "Checking that the correct number of folders is displayed in the Desktop Bookmarks folder"); // Three folders and "Up to Bookmarks".
} else { // On phones it's just the 4 folders mAsserter.is(adapter.getCount(), 4, "Checking that the correct number of folders is displayed in the Desktop Bookmarks folder");
mAsserter.is(adapter.getCount(), 4, "Checking that the correct number of folders is displayed in the Desktop Bookmarks folder");
}
clickOnBookmarkFolder(StringHelper.TOOLBAR_FOLDER_LABEL); clickOnBookmarkFolder(StringHelper.TOOLBAR_FOLDER_LABEL);

View File

@ -492,6 +492,23 @@ function stripHttpAndTrim(spec) {
return spec; return spec;
} }
/**
* Make a moz-action: URL for a given action and set of parameters.
*
* @param action
* Name of the action
* @param params
* Object, whose keys are parameter names and values are the
* corresponding parameter values.
* @return String representation of the built moz-action: URL
*/
function makeActionURL(action, params) {
let url = "moz-action:" + action + "," + JSON.stringify(params);
// Make a nsIURI out of this to ensure it's encoded properly.
return NetUtil.newURI(url).spec;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
//// Search Class //// Search Class
//// Manages a single instance of an autocomplete search. //// Manages a single instance of an autocomplete search.
@ -965,9 +982,12 @@ Search.prototype = {
// If actions are enabled and the page is open, add only the switch-to-tab // If actions are enabled and the page is open, add only the switch-to-tab
// result. Otherwise, add the normal result. // result. Otherwise, add the normal result.
let [url, action] = this._enableActions && openPageCount > 0 ? let url = escapedURL;
["moz-action:switchtab," + escapedURL, "switchtab"] : let action = null;
[escapedURL, null]; if (this._enableActions && openPageCount > 0) {
url = makeActionURL("switchtab", {url: escapedURL});
action = "switchtab";
}
// Always prefer the bookmark title unless it is empty // Always prefer the bookmark title unless it is empty
let title = bookmarkTitle || historyTitle; let title = bookmarkTitle || historyTitle;
@ -1347,12 +1367,15 @@ UnifiedComplete.prototype = {
let search = this._currentSearch; let search = this._currentSearch;
this.getDatabaseHandle().then(conn => search.execute(conn)) this.getDatabaseHandle().then(conn => search.execute(conn))
.then(null, ex => {
dump(`Query failed: ${ex}\n`);
Cu.reportError(ex);
})
.then(() => { .then(() => {
if (search == this._currentSearch) { if (search == this._currentSearch) {
this.finishSearch(true); this.finishSearch(true);
} }
}, ex => { dump("Query failed: " + ex + "\n"); });
Cu.reportError(ex); });
}, },
stopSearch: function () { stopSearch: function () {

View File

@ -281,3 +281,8 @@ function stripPrefix(spec)
} }
return spec; return spec;
} }
function makeActionURI(action, params) {
let url = "moz-action:" + action + "," + JSON.stringify(params);
return NetUtil.newURI(url);
}

View File

@ -22,14 +22,14 @@ add_task(function* test_tab_matches() {
yield check_autocomplete({ yield check_autocomplete({
search: "abc.com", search: "abc.com",
searchParam: "enable-actions", searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,http://abc.com/"), title: "ABC rocks" } ] matches: [ { uri: makeActionURI("switchtab", {url: "http://abc.com/"}), title: "ABC rocks" } ]
}); });
do_log_info("two results, one tab match"); do_log_info("two results, one tab match");
yield check_autocomplete({ yield check_autocomplete({
search: "abc", search: "abc",
searchParam: "enable-actions", searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,http://abc.com/"), title: "ABC rocks" }, matches: [ { uri: makeActionURI("switchtab", {url: "http://abc.com/"}), title: "ABC rocks" },
{ uri: uri2, title: "xyz.net - we're better than ABC" } ] { uri: uri2, title: "xyz.net - we're better than ABC" } ]
}); });
@ -38,8 +38,8 @@ add_task(function* test_tab_matches() {
yield check_autocomplete({ yield check_autocomplete({
search: "abc", search: "abc",
searchParam: "enable-actions", searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,http://abc.com/"), title: "ABC rocks" }, matches: [ { uri: makeActionURI("switchtab", {url: "http://abc.com/"}), title: "ABC rocks" },
{ uri: NetUtil.newURI("moz-action:switchtab,http://xyz.net/"), title: "xyz.net - we're better than ABC" } ] { uri: makeActionURI("switchtab", {url: "http://xyz.net/"}), title: "xyz.net - we're better than ABC" } ]
}); });
do_log_info("two results, both tab matches, one has multiple tabs"); do_log_info("two results, both tab matches, one has multiple tabs");
@ -47,8 +47,8 @@ add_task(function* test_tab_matches() {
yield check_autocomplete({ yield check_autocomplete({
search: "abc", search: "abc",
searchParam: "enable-actions", searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,http://abc.com/"), title: "ABC rocks" }, matches: [ { uri: makeActionURI("switchtab", {url: "http://abc.com/"}), title: "ABC rocks" },
{ uri: NetUtil.newURI("moz-action:switchtab,http://xyz.net/"), title: "xyz.net - we're better than ABC" } ] { uri: makeActionURI("switchtab", {url: "http://xyz.net/"}), title: "xyz.net - we're better than ABC" } ]
}); });
do_log_info("two results, no tab matches"); do_log_info("two results, no tab matches");
@ -66,30 +66,30 @@ add_task(function* test_tab_matches() {
yield check_autocomplete({ yield check_autocomplete({
search: gTabRestrictChar + " abc", search: gTabRestrictChar + " abc",
searchParam: "enable-actions", searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,http://abc.com/"), title: "ABC rocks" } ] matches: [ { uri: makeActionURI("switchtab", {url: "http://abc.com/"}), title: "ABC rocks" } ]
}); });
do_log_info("tab match with not-addable pages"); do_log_info("tab match with not-addable pages");
yield check_autocomplete({ yield check_autocomplete({
search: "mozilla", search: "mozilla",
searchParam: "enable-actions", searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,about:mozilla"), title: "about:mozilla" } ] matches: [ { uri: makeActionURI("switchtab", {url: "about:mozilla"}), title: "about:mozilla" } ]
}); });
do_log_info("tab match with not-addable pages and restriction character"); do_log_info("tab match with not-addable pages and restriction character");
yield check_autocomplete({ yield check_autocomplete({
search: gTabRestrictChar + " mozilla", search: gTabRestrictChar + " mozilla",
searchParam: "enable-actions", searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,about:mozilla"), title: "about:mozilla" } ] matches: [ { uri: makeActionURI("switchtab", {url: "about:mozilla"}), title: "about:mozilla" } ]
}); });
do_log_info("tab match with not-addable pages and only restriction character"); do_log_info("tab match with not-addable pages and only restriction character");
yield check_autocomplete({ yield check_autocomplete({
search: gTabRestrictChar, search: gTabRestrictChar,
searchParam: "enable-actions", searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,http://abc.com/"), title: "ABC rocks" }, matches: [ { uri: makeActionURI("switchtab", {url: "http://abc.com/"}), title: "ABC rocks" },
{ uri: NetUtil.newURI("moz-action:switchtab,about:mozilla"), title: "about:mozilla" }, { uri: makeActionURI("switchtab", {url: "about:mozilla"}), title: "about:mozilla" },
{ uri: NetUtil.newURI("moz-action:switchtab,data:text/html,test"), title: "data:text/html,test" } ] { uri: makeActionURI("switchtab", {url: "data:text/html,test"}), title: "data:text/html,test" } ]
}); });
yield cleanup(); yield cleanup();

View File

@ -1445,9 +1445,17 @@ extends="chrome://global/content/bindings/popup.xml#popup">
<method name="_adjustAcItem"> <method name="_adjustAcItem">
<body> <body>
<![CDATA[ <![CDATA[
var url = this.getAttribute("url"); let url = this.getAttribute("url");
var title = this.getAttribute("title"); let title = this.getAttribute("title");
var type = this.getAttribute("type"); let type = this.getAttribute("type");
let emphasiseTitle = true;
let emphasiseUrl = true;
// Hide the title's extra box by default, until we find out later if
// we need extra stuff.
this._extraBox.hidden = true;
this._titleBox.flex = 1;
this.removeAttribute("actiontype"); this.removeAttribute("actiontype");
@ -1457,34 +1465,36 @@ extends="chrome://global/content/bindings/popup.xml#popup">
// space when hidden. Setting the hidden property accomplishes that. // space when hidden. Setting the hidden property accomplishes that.
this._titleOverflowEllipsis.hidden = false; this._titleOverflowEllipsis.hidden = false;
let types = new Set(type.split(/\s+/));
// If the type includes an action, set up the item appropriately. // If the type includes an action, set up the item appropriately.
var types = type.split(/\s+/); if (types.has("action")) {
var actionIndex = types.indexOf("action"); let action = this._parseActionUrl(url);
if (actionIndex >= 0) { this.setAttribute("actiontype", action.type);
let [,action, param] = url.match(/^moz-action:([^,]+),(.*)$/);
this.setAttribute("actiontype", action); if (action.type == "switchtab") {
url = param; url = action.params.url;
let desc = this._stringBundle.GetStringFromName("switchToTab"); let desc = this._stringBundle.GetStringFromName("switchToTab");
this._setUpDescription(this._action, desc, true); this._setUpDescription(this._action, desc, true);
}
// Remove the "action" substring so that the correct style, if any, // Remove the "action" substring so that the correct style, if any,
// is applied below. // is applied below.
types.splice(actionIndex, 1); types.delete("action");
type = types.join(" ");
} }
// Check if we have a search engine name // Check if we have a search engine name
let searchEngine = ""; if (types.has("search")) {
let searchIndex = types.indexOf("search"); emphasiseUrl = false;
if (searchIndex >= 0) {
const TITLE_SEARCH_ENGINE_SEPARATOR = " \u00B7\u2013\u00B7 "; const TITLE_SEARCH_ENGINE_SEPARATOR = " \u00B7\u2013\u00B7 ";
[title, searchEngine] = title.split(TITLE_SEARCH_ENGINE_SEPARATOR); [title, searchEngine] = title.split(TITLE_SEARCH_ENGINE_SEPARATOR);
url = this._stringBundle.formatStringFromName("searchWithEngine", [searchEngine], 1);
// Remove the "search" substring so that the correct style, if any, // Remove the "search" substring so that the correct style, if any,
// is applied below. // is applied below.
types.splice(searchIndex, 1); types.delete("search");
type = types.join(" ");
} }
// If we have a tag match, show the tags and icon // If we have a tag match, show the tags and icon
@ -1531,10 +1541,6 @@ extends="chrome://global/content/bindings/popup.xml#popup">
// Don't emphasize keyword searches in the title or url // Don't emphasize keyword searches in the title or url
this.setAttribute("text", ""); this.setAttribute("text", "");
} else {
// Hide the title's extra box if we don't need extra stuff
this._extraBox.hidden = true;
this._titleBox.flex = 1;
} }
// Give the image the icon style and a special one for the type // Give the image the icon style and a special one for the type
@ -1546,15 +1552,8 @@ extends="chrome://global/content/bindings/popup.xml#popup">
title = url; title = url;
// Emphasize the matching search terms for the description // Emphasize the matching search terms for the description
this._setUpDescription(this._title, title); this._setUpDescription(this._title, title, !emphasiseTitle);
if (!searchEngine) { this._setUpDescription(this._url, url, !emphasiseUrl);
this._setUpDescription(this._url, url);
} else {
let desc = this._stringBundle.formatStringFromName("searchWithEngine", [searchEngine], 1);
// The search engine name, when present, is not emphasized.
this._setUpDescription(this._url, desc, true);
}
// Set up overflow on a timeout because the contents of the box // Set up overflow on a timeout because the contents of the box
// might not have a width yet even though we just changed them // might not have a width yet even though we just changed them
@ -1564,6 +1563,37 @@ extends="chrome://global/content/bindings/popup.xml#popup">
</body> </body>
</method> </method>
<method name="_parseActionUrl">
<parameter name="aUrl"/>
<body><![CDATA[
if (!aUrl.startsWith("moz-action:"))
return null;
// URL is in the format moz-action:ACTION,PARAMS
// Where PARAMS is a JSON encoded object.
aUrl = decodeURI(aUrl);
let [, type, params] = aUrl.match(/^moz-action:([^,]+),(.*)$/);
let action = {
type: type,
};
try {
action.params = JSON.parse(params);
} catch (e) {
// If this failed, we assume that params is not a JSON object, and
// is instead just a flat string. This will happen when
// UnifiedComplete is disabled - in which case, the param is always
// a URL.
action.params = {
url: params,
}
}
return action;
]]></body>
</method>
<method name="_setUpOverflow"> <method name="_setUpOverflow">
<parameter name="aParentBox"/> <parameter name="aParentBox"/>
<parameter name="aEllipsis"/> <parameter name="aEllipsis"/>

View File

@ -5433,6 +5433,11 @@ function getInnerId(window) {
getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID; getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
}; };
function getInnerId(window) {
return window.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
};
const symbolProtoToString = typeof Symbol === "function" ? Symbol.prototype.toString : null; const symbolProtoToString = typeof Symbol === "function" ? Symbol.prototype.toString : null;
function getSymbolName(symbol) { function getSymbolName(symbol) {

View File

@ -43,6 +43,11 @@ function getWindowID(window) {
* Browser-specific actors. * Browser-specific actors.
*/ */
function getInnerId(window) {
return window.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
};
/** /**
* Yield all windows of type |aWindowType|, from the oldest window to the * Yield all windows of type |aWindowType|, from the oldest window to the
* youngest, using nsIWindowMediator::getEnumerator. We're usually * youngest, using nsIWindowMediator::getEnumerator. We're usually

View File

@ -4539,7 +4539,8 @@ bool
mozilla::BrowserTabsRemoteAutostart() mozilla::BrowserTabsRemoteAutostart()
{ {
if (!gBrowserTabsRemoteAutostartInitialized) { if (!gBrowserTabsRemoteAutostartInitialized) {
bool prefEnabled = Preferences::GetBool("browser.tabs.remote.autostart", false); bool prefEnabled = Preferences::GetBool("browser.tabs.remote.autostart", false) ||
Preferences::GetBool("browser.tabs.remote.autostart.1", false);
bool disabledForA11y = Preferences::GetBool("browser.tabs.remote.autostart.disabled-because-using-a11y", false); bool disabledForA11y = Preferences::GetBool("browser.tabs.remote.autostart.disabled-because-using-a11y", false);
gBrowserTabsRemoteAutostart = !gSafeMode && !disabledForA11y && prefEnabled; gBrowserTabsRemoteAutostart = !gSafeMode && !disabledForA11y && prefEnabled;
gBrowserTabsRemoteAutostartInitialized = true; gBrowserTabsRemoteAutostartInitialized = true;