зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to s-c.
This commit is contained in:
Коммит
eeca52b172
|
@ -3084,7 +3084,7 @@ Accessible::GetAttrValue(nsIAtom *aProperty, double *aValue)
|
|||
uint32_t
|
||||
Accessible::GetActionRule()
|
||||
{
|
||||
if (InteractiveState() & states::UNAVAILABLE)
|
||||
if (!HasOwnContent() || (InteractiveState() & states::UNAVAILABLE))
|
||||
return eNoAction;
|
||||
|
||||
// Check if it's simple xlink.
|
||||
|
|
|
@ -130,7 +130,9 @@ function openBrowserWindowIntl()
|
|||
"_blank", params,
|
||||
gBrowserContext.startURL);
|
||||
|
||||
addA11yLoadEvent(startBrowserTests, browserWindow());
|
||||
whenDelayedStartupFinished(browserWindow(), function () {
|
||||
addA11yLoadEvent(startBrowserTests, browserWindow());
|
||||
});
|
||||
}
|
||||
|
||||
function startBrowserTests()
|
||||
|
@ -140,3 +142,12 @@ function startBrowserTests()
|
|||
else
|
||||
gBrowserContext.testFunc();
|
||||
}
|
||||
|
||||
function whenDelayedStartupFinished(aWindow, aCallback) {
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic) {
|
||||
if (aWindow == aSubject) {
|
||||
Services.obs.removeObserver(observer, aTopic);
|
||||
setTimeout(aCallback, 0);
|
||||
}
|
||||
}, "browser-delayed-startup-finished", false);
|
||||
}
|
||||
|
|
|
@ -1008,7 +1008,6 @@ var gBrowserInit = {
|
|||
if ("arguments" in window && window.arguments[0])
|
||||
var uriToLoad = window.arguments[0];
|
||||
|
||||
var isLoadingBlank = isBlankPageURL(uriToLoad);
|
||||
var mustLoadSidebar = false;
|
||||
|
||||
gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false);
|
||||
|
@ -1095,44 +1094,6 @@ var gBrowserInit = {
|
|||
// setup simple gestures support
|
||||
gGestureSupport.init(true);
|
||||
|
||||
|
||||
if (uriToLoad && uriToLoad != "about:blank") {
|
||||
if (uriToLoad instanceof Ci.nsISupportsArray) {
|
||||
let count = uriToLoad.Count();
|
||||
let specs = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
let urisstring = uriToLoad.GetElementAt(i).QueryInterface(Ci.nsISupportsString);
|
||||
specs.push(urisstring.data);
|
||||
}
|
||||
|
||||
// This function throws for certain malformed URIs, so use exception handling
|
||||
// so that we don't disrupt startup
|
||||
try {
|
||||
gBrowser.loadTabs(specs, false, true);
|
||||
} catch (e) {}
|
||||
}
|
||||
else if (uriToLoad instanceof XULElement) {
|
||||
// swap the given tab with the default about:blank tab and then close
|
||||
// the original tab in the other window.
|
||||
|
||||
// Stop the about:blank load
|
||||
gBrowser.stop();
|
||||
// make sure it has a docshell
|
||||
gBrowser.docShell;
|
||||
|
||||
gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad);
|
||||
}
|
||||
else if (window.arguments.length >= 3) {
|
||||
loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null,
|
||||
window.arguments[4] || false);
|
||||
window.focus();
|
||||
}
|
||||
// Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
|
||||
// Such callers expect that window.arguments[0] is handled as a single URI.
|
||||
else
|
||||
loadOneOrMoreURIs(uriToLoad);
|
||||
}
|
||||
|
||||
if (window.opener && !window.opener.closed) {
|
||||
let openerSidebarBox = window.opener.document.getElementById("sidebar-box");
|
||||
// If the opener had a sidebar, open the same sidebar in our window.
|
||||
|
@ -1242,7 +1203,7 @@ var gBrowserInit = {
|
|||
retrieveToolbarIconsizesFromTheme();
|
||||
|
||||
// Wait until chrome is painted before executing code not critical to making the window visible
|
||||
this._boundDelayedStartup = this._delayedStartup.bind(this, isLoadingBlank, mustLoadSidebar);
|
||||
this._boundDelayedStartup = this._delayedStartup.bind(this, uriToLoad, mustLoadSidebar);
|
||||
window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
|
||||
|
||||
gStartupRan = true;
|
||||
|
@ -1253,7 +1214,7 @@ var gBrowserInit = {
|
|||
this._boundDelayedStartup = null;
|
||||
},
|
||||
|
||||
_delayedStartup: function(isLoadingBlank, mustLoadSidebar) {
|
||||
_delayedStartup: function(uriToLoad, mustLoadSidebar) {
|
||||
let tmp = {};
|
||||
Cu.import("resource:///modules/TelemetryTimestamps.jsm", tmp);
|
||||
let TelemetryTimestamps = tmp.TelemetryTimestamps;
|
||||
|
@ -1261,6 +1222,45 @@ var gBrowserInit = {
|
|||
|
||||
this._cancelDelayedStartup();
|
||||
|
||||
var isLoadingBlank = isBlankPageURL(uriToLoad);
|
||||
|
||||
if (uriToLoad && uriToLoad != "about:blank") {
|
||||
if (uriToLoad instanceof Ci.nsISupportsArray) {
|
||||
let count = uriToLoad.Count();
|
||||
let specs = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
let urisstring = uriToLoad.GetElementAt(i).QueryInterface(Ci.nsISupportsString);
|
||||
specs.push(urisstring.data);
|
||||
}
|
||||
|
||||
// This function throws for certain malformed URIs, so use exception handling
|
||||
// so that we don't disrupt startup
|
||||
try {
|
||||
gBrowser.loadTabs(specs, false, true);
|
||||
} catch (e) {}
|
||||
}
|
||||
else if (uriToLoad instanceof XULElement) {
|
||||
// swap the given tab with the default about:blank tab and then close
|
||||
// the original tab in the other window.
|
||||
|
||||
// Stop the about:blank load
|
||||
gBrowser.stop();
|
||||
// make sure it has a docshell
|
||||
gBrowser.docShell;
|
||||
|
||||
gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad);
|
||||
}
|
||||
else if (window.arguments.length >= 3) {
|
||||
loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null,
|
||||
window.arguments[4] || false);
|
||||
window.focus();
|
||||
}
|
||||
// Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
|
||||
// Such callers expect that window.arguments[0] is handled as a single URI.
|
||||
else
|
||||
loadOneOrMoreURIs(uriToLoad);
|
||||
}
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
// Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
|
||||
setTimeout(function() { SafeBrowsing.init(); }, 2000);
|
||||
|
|
|
@ -874,8 +874,13 @@
|
|||
this.showTab(this.mCurrentTab);
|
||||
|
||||
var backForwardContainer = document.getElementById("unified-back-forward-button");
|
||||
if (backForwardContainer)
|
||||
if (backForwardContainer) {
|
||||
backForwardContainer.setAttribute("switchingtabs", "true");
|
||||
window.addEventListener("MozAfterPaint", function removeSwitchingtabsAttr() {
|
||||
window.removeEventListener("MozAfterPaint", removeSwitchingtabsAttr);
|
||||
backForwardContainer.removeAttribute("switchingtabs");
|
||||
});
|
||||
}
|
||||
|
||||
if (updatePageReport)
|
||||
this.mCurrentBrowser.updatePageReport();
|
||||
|
@ -946,9 +951,6 @@
|
|||
true, false);
|
||||
}
|
||||
|
||||
if (backForwardContainer)
|
||||
backForwardContainer.removeAttribute("switchingtabs");
|
||||
|
||||
if (this.mCurrentTab.selected)
|
||||
this._setCloseKeyState(!this.mCurrentTab.pinned);
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
!define URLStubDownload "http://download.mozilla.org/?product=firefox-aurora-latest&os=win&lang=${AB_CD}"
|
||||
!define URLManualDownload "https://www.mozilla.org/firefox/installer-help/?channel=aurora"
|
||||
!define Channel "aurora"
|
||||
|
||||
# The installer's certificate name and issuer expected by the stub installer
|
||||
!define CertNameDownload "Mozilla Corporation"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
!define URLStubDownload "http://download.mozilla.org/?product=firefox-nightly-latest&os=win&lang=${AB_CD}"
|
||||
!define URLManualDownload "https://www.mozilla.org/firefox/installer-help/?channel=nightly"
|
||||
!define Channel "nightly"
|
||||
|
||||
# The installer's certificate name and issuer expected by the stub installer
|
||||
!define CertNameDownload "Mozilla Corporation"
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
!define Official
|
||||
!define URLStubDownload "http://download.mozilla.org/?product=firefox-latest&os=win&lang=${AB_CD}"
|
||||
!define URLManualDownload "https://www.mozilla.org/firefox/installer-help/?channel=release"
|
||||
!define Channel "release"
|
||||
|
||||
# The installer's certificate name and issuer expected by the stub installer
|
||||
!define CertNameDownload "Mozilla Corporation"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
!define URLStubDownload "http://download.mozilla.org/?product=firefox-latest&os=win&lang=${AB_CD}"
|
||||
!define URLManualDownload "https://www.mozilla.org/firefox/installer-help/?channel=release"
|
||||
!define Channel "unofficial"
|
||||
|
||||
# The installer's certificate name and issuer expected by the stub installer
|
||||
!define CertNameDownload "Mozilla Corporation"
|
||||
|
|
|
@ -20,10 +20,10 @@ XPCOMUtils.defineLazyModuleGetter(this,
|
|||
"DebuggerServer", "resource://gre/modules/devtools/dbg-server.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this,
|
||||
"Services", "resource:///modules/Services.jsm");
|
||||
"Services", "resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this,
|
||||
"FileUtils", "resource:///modules/FileUtils.jsm");
|
||||
"FileUtils", "resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["DebuggerUI"];
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
!define BETA_UPDATE_CHANNEL
|
||||
!endif
|
||||
|
||||
!define BaseURLStubPing "http://download-stats.mozilla.org/stub/v1/"
|
||||
|
||||
# NO_INSTDIR_FROM_REG is defined for pre-releases which have a PreReleaseSuffix
|
||||
# (e.g. Alpha X, Beta X, etc.) to prevent finding a non-default installation
|
||||
# directory in the registry and using that as the default. This prevents
|
||||
|
|
|
@ -33,6 +33,7 @@ Var CheckboxSetAsDefault
|
|||
Var CheckboxShortcutOnBar ; Used for Quicklaunch or Taskbar as appropriate
|
||||
Var CheckboxShortcutInStartMenu
|
||||
Var CheckboxShortcutOnDesktop
|
||||
Var CheckboxSendPing
|
||||
Var CheckboxInstallMaintSvc
|
||||
Var DirRequest
|
||||
Var ButtonBrowse
|
||||
|
@ -66,9 +67,25 @@ Var CanSetAsDefault
|
|||
Var TmpVal
|
||||
Var InstallCounterStep
|
||||
|
||||
Var ExitCode
|
||||
Var SecondsToDownload
|
||||
Var ExistingProfile
|
||||
Var ExistingInstall
|
||||
Var DownloadedAmount
|
||||
Var FirefoxLaunch
|
||||
|
||||
Var HEIGHT_PX
|
||||
Var CTL_RIGHT_PX
|
||||
|
||||
!define ERR_SUCCESS 0
|
||||
!define ERR_CANCEL_DOWNLOAD 10
|
||||
!define ERR_INVALID_HANDLE 11
|
||||
!define ERR_CERT_UNTRUSTED 12
|
||||
!define ERR_CERT_ATTRIBUTES 13
|
||||
!define ERR_CERT_UNTRUSTED_AND_ATTRIBUTES 14
|
||||
!define ERR_CHECK_INSTALL_TIMEOUT 15
|
||||
!define ERR_UNKNOWN 99
|
||||
|
||||
!define DownloadIntervalMS 200 ; Interval for the download timer
|
||||
!define InstallIntervalMS 100 ; Interval for the install timer
|
||||
|
||||
|
@ -122,6 +139,8 @@ Var CTL_RIGHT_PX
|
|||
!define URLStubDownload "http://download.mozilla.org/?product=firefox-beta-latest&os=win&lang=${AB_CD}"
|
||||
!undef URLManualDownload
|
||||
!define URLManualDownload "https://www.mozilla.org/firefox/installer-help/?channel=beta"
|
||||
!undef Channel
|
||||
!define Channel "beta"
|
||||
!endif
|
||||
!endif
|
||||
|
||||
|
@ -296,6 +315,10 @@ Function .onInit
|
|||
StrCpy $CanSetAsDefault "true"
|
||||
${EndIf}
|
||||
|
||||
StrCpy $IsDownloadFinished ""
|
||||
StrCpy $FirefoxLaunch 0
|
||||
StrCpy $ExitCode ${ERR_UNKNOWN}
|
||||
|
||||
CreateFont $FontBlurb "$(^Font)" "12" "500"
|
||||
CreateFont $FontNormal "$(^Font)" "11" "500"
|
||||
CreateFont $FontItalic "$(^Font)" "11" "500" /ITALIC
|
||||
|
@ -326,6 +349,22 @@ FunctionEnd
|
|||
!endif
|
||||
|
||||
Function .onGUIEnd
|
||||
; The value of $IsDownloadFinished will be false if the download was attempted
|
||||
; and wasn't completed. Get the seconds elapsed trying to download.
|
||||
${If} $IsDownloadFinished == "false"
|
||||
Call GetSecondsToDownload
|
||||
${EndIf}
|
||||
|
||||
; Try to send a ping if a download was attempted
|
||||
${If} $IsDownloadFinished != ""
|
||||
${AndIf} $CheckboxSendPing == 1
|
||||
System::Int64Op $DownloadedAmount / 1024
|
||||
Pop $DownloadedAmount
|
||||
InetBgDL::Get "${BaseURLStubPing}${Channel}/${AB_CD}/$ExitCode/" \
|
||||
"$FirefoxLaunch/$SecondsToDownload/$DownloadedAmount/" \
|
||||
"$ExistingProfile/$ExistingInstall/" "$PLUGINSDIR\_temp" /END
|
||||
${EndIf}
|
||||
|
||||
${UnloadUAC}
|
||||
FunctionEnd
|
||||
|
||||
|
@ -337,6 +376,7 @@ Function .onUserAbort
|
|||
${NSD_KillTimer} FinishInstall
|
||||
${NSD_KillTimer} DisplayDownloadError
|
||||
|
||||
Delete "$PLUGINSDIR\_temp"
|
||||
Delete "$PLUGINSDIR\download.exe"
|
||||
Delete "$PLUGINSDIR\${CONFIG_INI}"
|
||||
FunctionEnd
|
||||
|
@ -350,6 +390,7 @@ Function createIntro
|
|||
StrCpy $CheckboxShortcutOnBar 1
|
||||
StrCpy $CheckboxShortcutInStartMenu 1
|
||||
StrCpy $CheckboxShortcutOnDesktop 1
|
||||
StrCpy $CheckboxSendPing 1
|
||||
!ifdef MOZ_MAINTENANCE_SERVICE
|
||||
StrCpy $CheckboxInstallMaintSvc 1
|
||||
!else
|
||||
|
@ -590,6 +631,16 @@ Function createOptions
|
|||
|
||||
Call UpdateFreeSpaceLabel
|
||||
|
||||
${NSD_CreateCheckbox} ${OPTIONS_ITEM_EDGE_DU} 168u ${OPTIONS_SUBITEM_WIDTH_DU} \
|
||||
12u "$(SEND_PING)"
|
||||
Pop $CheckboxSendPing
|
||||
; The uxtheme must be disabled on checkboxes in order to override the system
|
||||
; font color.
|
||||
System::Call 'uxtheme::SetWindowTheme(i $CheckboxSendPing, w " ", w " ")'
|
||||
SetCtlColors $CheckboxSendPing ${OPTIONS_TEXT_COLOR_NORMAL} ${OPTIONS_BKGRD_COLOR}
|
||||
SendMessage $CheckboxSendPing ${WM_SETFONT} $FontNormal 0
|
||||
${NSD_Check} $CheckboxSendPing
|
||||
|
||||
!ifdef MOZ_MAINTENANCE_SERVICE
|
||||
; Only show the maintenance service checkbox if we have write access to HKLM
|
||||
Call IsUserAdmin
|
||||
|
@ -607,7 +658,7 @@ Function createOptions
|
|||
ClearErrors
|
||||
ReadRegStr $0 HKLM "SYSTEM\CurrentControlSet\services\MozillaMaintenance" "ImagePath"
|
||||
${If} ${Errors}
|
||||
${NSD_CreateCheckbox} ${OPTIONS_ITEM_EDGE_DU} 175u ${OPTIONS_ITEM_WIDTH_DU} \
|
||||
${NSD_CreateCheckbox} ${OPTIONS_ITEM_EDGE_DU} 184u ${OPTIONS_ITEM_WIDTH_DU} \
|
||||
12u "$(INSTALL_MAINT_SERVICE)"
|
||||
Pop $CheckboxInstallMaintSvc
|
||||
System::Call 'uxtheme::SetWindowTheme(i $CheckboxInstallMaintSvc, w " ", w " ")'
|
||||
|
@ -668,6 +719,7 @@ Function leaveOptions
|
|||
${NSD_GetState} $CheckboxShortcutOnBar $CheckboxShortcutOnBar
|
||||
${NSD_GetState} $CheckboxShortcutInStartMenu $CheckboxShortcutInStartMenu
|
||||
${NSD_GetState} $CheckboxShortcutOnDesktop $CheckboxShortcutOnDesktop
|
||||
${NSD_GetState} $CheckboxSendPing $CheckboxSendPing
|
||||
!ifdef MOZ_MAINTENANCE_SERVICE
|
||||
${NSD_GetState} $CheckboxInstallMaintSvc $CheckboxInstallMaintSvc
|
||||
!endif
|
||||
|
@ -816,7 +868,29 @@ Function createInstall
|
|||
SetCtlColors $0 ${FOOTER_CONTROL_TEXT_COLOR_FADED} ${FOOTER_BKGRD_COLOR}
|
||||
ShowWindow $0 ${SW_SHOW}
|
||||
|
||||
StrCpy $IsDownloadFinished "false"
|
||||
StrCpy $DownloadReset "false"
|
||||
StrCpy $ExitCode ${ERR_CANCEL_DOWNLOAD}
|
||||
${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
|
||||
StrCpy $ExistingInstall 1
|
||||
${Else}
|
||||
StrCpy $ExistingInstall 0
|
||||
${EndIf}
|
||||
|
||||
${If} ${FileExists} "$LOCALAPPDATA\Mozilla\Firefox"
|
||||
StrCpy $ExistingProfile 1
|
||||
${Else}
|
||||
StrCpy $ExistingProfile 0
|
||||
${EndIf}
|
||||
|
||||
GetTempFileName $2
|
||||
GetFileTime $2 $1 $0
|
||||
Delete $2
|
||||
System::Int64Op $1 * 0x100000000
|
||||
Pop $1
|
||||
System::Int64Op $1 + $0
|
||||
Pop $SecondsToDownload
|
||||
|
||||
${NSD_CreateTimer} StartDownload ${DownloadIntervalMS}
|
||||
|
||||
LockWindow off
|
||||
|
@ -833,6 +907,24 @@ Function leaveInstall
|
|||
# Need a ping?
|
||||
FunctionEnd
|
||||
|
||||
; This function is not idempotent. It calculates the amount of time between now
|
||||
; and $SecondsToDownload and stores the results back into $SecondsToDownload.
|
||||
; For that reason it should only be called once for the purpose of determining
|
||||
; the number of elapsed seconds to download.
|
||||
Function GetSecondsToDownload
|
||||
GetTempFileName $2
|
||||
GetFileTime $2 $1 $0
|
||||
Delete $2
|
||||
System::Int64Op $1 * 0x100000000
|
||||
Pop $1
|
||||
System::Int64Op $1 + $0
|
||||
Pop $0
|
||||
System::Int64Op $0 - $SecondsToDownload
|
||||
Pop $SecondsToDownload
|
||||
System::Int64Op $SecondsToDownload / 10000000
|
||||
Pop $SecondsToDownload
|
||||
FunctionEnd
|
||||
|
||||
Function StartDownload
|
||||
${NSD_KillTimer} StartDownload
|
||||
InetBgDL::Get "${URLStubDownload}" "$PLUGINSDIR\download.exe" /END
|
||||
|
@ -855,6 +947,7 @@ Function OnDownload
|
|||
${If} $0 > 299
|
||||
${NSD_KillTimer} OnDownload
|
||||
${If} "$DownloadReset" != "true"
|
||||
StrCpy $DownloadedAmount 0
|
||||
${NSD_AddStyle} $ProgressbarDownload ${PBS_MARQUEE}
|
||||
SendMessage $ProgressbarDownload ${PBM_SETMARQUEE} 1 10 ; start=1|stop=0 interval(ms)=+N
|
||||
${EndIf}
|
||||
|
@ -893,6 +986,8 @@ Function OnDownload
|
|||
; InstallProgressFirstStep define and provides the user with immediate
|
||||
; feedback.
|
||||
StrCpy $InstallCounterStep ${InstallProgressFirstStep}
|
||||
Call GetSecondsToDownload
|
||||
StrCpy $DownloadedAmount $DownloadSize
|
||||
LockWindow on
|
||||
; Update the progress bars first in the UI change so they take affect
|
||||
; before other UI changes.
|
||||
|
@ -920,6 +1015,7 @@ Function OnDownload
|
|||
StrCpy $HandleDownload "$R9"
|
||||
|
||||
${If} $HandleDownload == ${INVALID_HANDLE_VALUE}
|
||||
StrCpy $ExitCode ${ERR_INVALID_HANDLE}
|
||||
StrCpy $0 "0"
|
||||
StrCpy $1 "0"
|
||||
${Else}
|
||||
|
@ -928,6 +1024,14 @@ Function OnDownload
|
|||
CertCheck::VerifyCertNameIssuer "$PLUGINSDIR\download.exe" \
|
||||
"${CertNameDownload}" "${CertIssuerDownload}"
|
||||
Pop $1
|
||||
${If} $0 == 0
|
||||
${AndIf} $1 == 0
|
||||
StrCpy $ExitCode ${ERR_CERT_UNTRUSTED_AND_ATTRIBUTES}
|
||||
${ElseIf} $0 == 0
|
||||
StrCpy $ExitCode ${ERR_CERT_UNTRUSTED}
|
||||
${ElseIf} $1 == 0
|
||||
StrCpy $ExitCode ${ERR_CERT_ATTRIBUTES}
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
${If} $0 == 0
|
||||
|
@ -998,6 +1102,7 @@ Function OnDownload
|
|||
ShowWindow $BitmapBlurb2 ${SW_SHOW}
|
||||
LockWindow off
|
||||
${EndIf}
|
||||
StrCpy $DownloadedAmount $3
|
||||
SendMessage $ProgressbarDownload ${PBM_SETPOS} $3 0
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
@ -1021,6 +1126,7 @@ Function CheckInstall
|
|||
${NSD_KillTimer} CheckInstall
|
||||
; Close the handle that prevents modification of the full installer
|
||||
System::Call 'kernel32::CloseHandle(i $HandleDownload)'
|
||||
StrCpy $ExitCode ${ERR_CHECK_INSTALL_TIMEOUT}
|
||||
; Use a timer so the UI has a chance to update
|
||||
${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS}
|
||||
Return
|
||||
|
@ -1095,6 +1201,8 @@ Function FinishInstall
|
|||
Rename "$INSTDIR\${FileMainEXE}.moz-upgrade" "$INSTDIR\${FileMainEXE}"
|
||||
${EndIf}
|
||||
|
||||
StrCpy $ExitCode ${ERR_SUCCESS}
|
||||
|
||||
Call LaunchApp
|
||||
|
||||
; The following will exit the installer
|
||||
|
@ -1309,10 +1417,13 @@ FunctionEnd
|
|||
Function LaunchApp
|
||||
FindWindow $0 "${WindowClass}"
|
||||
${If} $0 <> 0 ; integer comparison
|
||||
StrCpy $FirefoxLaunch 1
|
||||
MessageBox MB_OK|MB_ICONQUESTION "$(WARN_MANUALLY_CLOSE_APP_LAUNCH)"
|
||||
Return
|
||||
${EndIf}
|
||||
|
||||
StrCpy $FirefoxLaunch 2
|
||||
|
||||
ClearErrors
|
||||
${GetParameters} $0
|
||||
${GetOptions} "$0" "/UAC:" $1
|
||||
|
|
|
@ -49,6 +49,7 @@ SPACE_REQUIRED=Space Required:
|
|||
SPACE_AVAILABLE=Space Available:
|
||||
ONE_MOMENT=One moment, $BrandShortName will launch as soon as the install is complete…
|
||||
INSTALL_MAINT_SERVICE=&Install the $BrandShortName background update service
|
||||
SEND_PING=S&end information about this installation to Mozilla
|
||||
BROWSE_BUTTON=B&rowse…
|
||||
DEST_FOLDER=Destination Folder
|
||||
|
||||
|
|
|
@ -2,12 +2,15 @@ simplejson.pth:python/simplejson-2.1.1
|
|||
manifestdestiny.pth:testing/mozbase/manifestdestiny
|
||||
mozcrash.pth:testing/mozbase/mozcrash
|
||||
mozdevice.pth:testing/mozbase/mozdevice
|
||||
mozfile.pth:testing/mozbase/mozfile
|
||||
mozhttpd.pth:testing/mozbase/mozhttpd
|
||||
mozinfo.pth:testing/mozbase/mozinfo
|
||||
mozinstall.pth:testing/mozbase/mozinstall
|
||||
mozlog.pth:testing/mozbase/mozlog
|
||||
mozprocess.pth:testing/mozbase/mozprocess
|
||||
mozprofile.pth:testing/mozbase/mozprofile
|
||||
mozrunner.pth:testing/mozbase/mozrunner
|
||||
marionette.pth:testing/marionette/client
|
||||
blessings.pth:python/blessings
|
||||
mozbuild.pth:python/mozbuild
|
||||
pymake.pth:build/pymake
|
||||
|
|
|
@ -64,6 +64,7 @@ class JarMaker(object):
|
|||
|
||||
ignore = re.compile('\s*(\#.*)?$')
|
||||
jarline = re.compile('(?:(?P<jarfile>[\w\d.\-\_\\\/]+).jar\:)|(?:\s*(\#.*)?)\s*$')
|
||||
relsrcline = re.compile('relativesrcdir\s+(?P<relativesrcdir>.+?):')
|
||||
regline = re.compile('\%\s+(.*)$')
|
||||
entryre = '(?P<optPreprocess>\*)?(?P<optOverwrite>\+?)\s+'
|
||||
entryline = re.compile(entryre + '(?P<output>[\w\d.\-\_\\\/\+\@]+)\s*(\((?P<locale>\%?)(?P<source>[\w\d.\-\_\\\/\@]+)\))?\s*$')
|
||||
|
@ -77,6 +78,9 @@ class JarMaker(object):
|
|||
self.topsourcedir = None
|
||||
self.sourcedirs = []
|
||||
self.localedirs = None
|
||||
self.l10nbase = None
|
||||
self.l10nmerge = None
|
||||
self.relativesrcdir = None
|
||||
|
||||
def getCommandLineParser(self):
|
||||
'''Get a optparse.OptionParser for jarmaker.
|
||||
|
@ -105,6 +109,12 @@ class JarMaker(object):
|
|||
help="top source directory")
|
||||
p.add_option('-c', '--l10n-src', type="string", action="append",
|
||||
help="localization directory")
|
||||
p.add_option('--l10n-base', type="string", action="store",
|
||||
help="base directory to be used for localization (requires relativesrcdir)")
|
||||
p.add_option('--locale-mergedir', type="string", action="store",
|
||||
help="base directory to be used for l10n-merge (requires l10n-base and relativesrcdir)")
|
||||
p.add_option('--relativesrcdir', type="string",
|
||||
help="relativesrcdir to be used for localization")
|
||||
p.add_option('-j', type="string",
|
||||
help="jarfile directory")
|
||||
return p
|
||||
|
@ -170,7 +180,7 @@ class JarMaker(object):
|
|||
mf.close()
|
||||
finally:
|
||||
lock = None
|
||||
|
||||
|
||||
def makeJar(self, infile, jardir):
|
||||
'''makeJar is the main entry point to JarMaker.
|
||||
|
||||
|
@ -183,6 +193,8 @@ class JarMaker(object):
|
|||
self.sourcedirs = [_normpath(p) for p in self.sourcedirs]
|
||||
if self.localedirs:
|
||||
self.localedirs = [_normpath(p) for p in self.localedirs]
|
||||
elif self.relativesrcdir:
|
||||
self.localedirs = self.generateLocaleDirs(self.relativesrcdir)
|
||||
if isinstance(infile, basestring):
|
||||
logging.info("processing " + infile)
|
||||
self.sourcedirs.append(_normpath(os.path.dirname(infile)))
|
||||
|
@ -205,6 +217,23 @@ class JarMaker(object):
|
|||
pass
|
||||
return
|
||||
|
||||
def generateLocaleDirs(self, relativesrcdir):
|
||||
if os.path.basename(relativesrcdir) == 'locales':
|
||||
# strip locales
|
||||
l10nrelsrcdir = os.path.dirname(relativesrcdir)
|
||||
else:
|
||||
l10nrelsrcdir = relativesrcdir
|
||||
locdirs = []
|
||||
# generate locales dirs, merge, l10nbase, en-US
|
||||
if self.l10nmerge:
|
||||
locdirs.append(os.path.join(self.l10nmerge, l10nrelsrcdir))
|
||||
if self.l10nbase:
|
||||
locdirs.append(os.path.join(self.l10nbase, l10nrelsrcdir))
|
||||
if self.l10nmerge or not self.l10nbase:
|
||||
# add en-US if we merge, or if it's not l10n
|
||||
locdirs.append(os.path.join(self.topsourcedir, relativesrcdir, 'en-US'))
|
||||
return locdirs
|
||||
|
||||
def processJarSection(self, jarfile, lines, jardir):
|
||||
'''Internal method called by makeJar to actually process a section
|
||||
of a jar.mn file.
|
||||
|
@ -254,6 +283,11 @@ class JarMaker(object):
|
|||
raise
|
||||
if self.ignore.match(l):
|
||||
continue
|
||||
m = self.relsrcline.match(l)
|
||||
if m:
|
||||
relativesrcdir = m.group('relativesrcdir')
|
||||
self.localedirs = self.generateLocaleDirs(relativesrcdir)
|
||||
continue
|
||||
m = self.regline.match(l)
|
||||
if m:
|
||||
rline = m.group(1)
|
||||
|
@ -323,7 +357,7 @@ class JarMaker(object):
|
|||
outf.write(inf.read())
|
||||
outf.close()
|
||||
inf.close()
|
||||
|
||||
|
||||
|
||||
class OutputHelper_jar(object):
|
||||
'''Provide getDestModTime and getOutput for a given jarfile.
|
||||
|
@ -402,6 +436,16 @@ def main():
|
|||
if options.bothManifests:
|
||||
jm.useChromeManifest = True
|
||||
jm.useJarfileManifest = True
|
||||
if options.l10n_base:
|
||||
if not options.relativesrcdir:
|
||||
p.error('relativesrcdir required when using l10n-base')
|
||||
if options.l10n_src:
|
||||
p.error('both l10n-src and l10n-base are not supported')
|
||||
jm.l10nbase = options.l10n_base
|
||||
jm.relativesrcdir = options.relativesrcdir
|
||||
jm.l10nmerge = options.locale_mergedir
|
||||
elif options.locale_mergedir:
|
||||
p.error('l10n-base required when using locale-mergedir')
|
||||
jm.localedirs = options.l10n_src
|
||||
noise = logging.INFO
|
||||
if options.verbose is not None:
|
||||
|
|
|
@ -703,17 +703,21 @@ ifdef relativesrcdir
|
|||
LOCALE_SRCDIR = $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir))
|
||||
endif
|
||||
|
||||
ifdef LOCALE_SRCDIR
|
||||
# if LOCALE_MERGEDIR is set, use mergedir first, then the localization,
|
||||
# and finally en-US
|
||||
ifdef relativesrcdir
|
||||
MAKE_JARS_FLAGS += --relativesrcdir=$(relativesrcdir)
|
||||
ifneq (en-US,$(AB_CD))
|
||||
ifdef LOCALE_MERGEDIR
|
||||
MAKE_JARS_FLAGS += -c $(LOCALE_MERGEDIR)/$(subst /locales,,$(relativesrcdir))
|
||||
MAKE_JARS_FLAGS += --locale-mergedir=$(LOCALE_MERGEDIR)
|
||||
endif
|
||||
ifdef IS_LANGUAGE_REPACK
|
||||
MAKE_JARS_FLAGS += --l10n-base=$(L10NBASEDIR)
|
||||
endif
|
||||
else
|
||||
MAKE_JARS_FLAGS += -c $(LOCALE_SRCDIR)
|
||||
ifdef LOCALE_MERGEDIR
|
||||
MAKE_JARS_FLAGS += -c $(topsrcdir)/$(relativesrcdir)/en-US
|
||||
endif
|
||||
endif
|
||||
endif # en-US
|
||||
else
|
||||
MAKE_JARS_FLAGS += -c $(LOCALE_SRCDIR)
|
||||
endif # ! relativesrcdir
|
||||
|
||||
ifdef LOCALE_MERGEDIR
|
||||
MERGE_FILE = $(firstword \
|
||||
|
|
|
@ -4,6 +4,7 @@ import os, sys, os.path, time, inspect
|
|||
from filecmp import dircmp
|
||||
from tempfile import mkdtemp
|
||||
from shutil import rmtree, copy2
|
||||
from StringIO import StringIO
|
||||
from zipfile import ZipFile
|
||||
import mozunit
|
||||
from JarMaker import JarMaker
|
||||
|
@ -240,5 +241,64 @@ class TestJarMaker(unittest.TestCase):
|
|||
"%s is not a symlink to %s" % (destfoo, srcbar))
|
||||
|
||||
|
||||
class Test_relativesrcdir(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.jm = JarMaker()
|
||||
self.jm.topsourcedir = '/TOPSOURCEDIR'
|
||||
self.jm.relativesrcdir = 'browser/locales'
|
||||
self.fake_empty_file = StringIO()
|
||||
self.fake_empty_file.name = 'fake_empty_file'
|
||||
def tearDown(self):
|
||||
del self.jm
|
||||
del self.fake_empty_file
|
||||
def test_en_US(self):
|
||||
jm = self.jm
|
||||
jm.makeJar(self.fake_empty_file, '/NO_OUTPUT_REQUIRED')
|
||||
self.assertEquals(jm.localedirs,
|
||||
[
|
||||
os.path.join(os.path.abspath('/TOPSOURCEDIR'),
|
||||
'browser/locales', 'en-US')
|
||||
])
|
||||
def test_l10n_no_merge(self):
|
||||
jm = self.jm
|
||||
jm.l10nbase = '/L10N_BASE'
|
||||
jm.makeJar(self.fake_empty_file, '/NO_OUTPUT_REQUIRED')
|
||||
self.assertEquals(jm.localedirs, [os.path.join('/L10N_BASE', 'browser')])
|
||||
def test_l10n_merge(self):
|
||||
jm = self.jm
|
||||
jm.l10nbase = '/L10N_BASE'
|
||||
jm.l10nmerge = '/L10N_MERGE'
|
||||
jm.makeJar(self.fake_empty_file, '/NO_OUTPUT_REQUIRED')
|
||||
self.assertEquals(jm.localedirs,
|
||||
[os.path.join('/L10N_MERGE', 'browser'),
|
||||
os.path.join('/L10N_BASE', 'browser'),
|
||||
os.path.join(os.path.abspath('/TOPSOURCEDIR'),
|
||||
'browser/locales', 'en-US')
|
||||
])
|
||||
def test_override(self):
|
||||
jm = self.jm
|
||||
jm.outputFormat = 'flat' # doesn't touch chrome dir without files
|
||||
jarcontents = StringIO('''en-US.jar:
|
||||
relativesrcdir dom/locales:
|
||||
''')
|
||||
jarcontents.name = 'override.mn'
|
||||
jm.makeJar(jarcontents, '/NO_OUTPUT_REQUIRED')
|
||||
self.assertEquals(jm.localedirs,
|
||||
[
|
||||
os.path.join(os.path.abspath('/TOPSOURCEDIR'),
|
||||
'dom/locales', 'en-US')
|
||||
])
|
||||
def test_override_l10n(self):
|
||||
jm = self.jm
|
||||
jm.l10nbase = '/L10N_BASE'
|
||||
jm.outputFormat = 'flat' # doesn't touch chrome dir without files
|
||||
jarcontents = StringIO('''en-US.jar:
|
||||
relativesrcdir dom/locales:
|
||||
''')
|
||||
jarcontents.name = 'override.mn'
|
||||
jm.makeJar(jarcontents, '/NO_OUTPUT_REQUIRED')
|
||||
self.assertEquals(jm.localedirs, [os.path.join('/L10N_BASE', 'dom')])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
mozunit.main()
|
||||
|
|
|
@ -2866,11 +2866,21 @@ public:
|
|||
}
|
||||
|
||||
mContext->MakeContextCurrent();
|
||||
WebGLuint renderbuffername = wrb ? wrb->GLName() : 0;
|
||||
WebGLuint parambuffername = wrb ? wrb->GLName() : 0;
|
||||
if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
|
||||
mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_DEPTH_ATTACHMENT, rbtarget, renderbuffername);
|
||||
mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_STENCIL_ATTACHMENT, rbtarget, renderbuffername);
|
||||
WebGLuint depthbuffername = parambuffername;
|
||||
WebGLuint stencilbuffername = parambuffername;
|
||||
if (!parambuffername){
|
||||
depthbuffername = mDepthAttachment.Renderbuffer() ? mDepthAttachment.Renderbuffer()->GLName() : 0;
|
||||
stencilbuffername = mStencilAttachment.Renderbuffer() ? mStencilAttachment.Renderbuffer()->GLName() : 0;
|
||||
}
|
||||
mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_DEPTH_ATTACHMENT, rbtarget, depthbuffername);
|
||||
mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_STENCIL_ATTACHMENT, rbtarget, stencilbuffername);
|
||||
} else {
|
||||
WebGLuint renderbuffername = parambuffername;
|
||||
if(!parambuffername && (attachment == LOCAL_GL_DEPTH_ATTACHMENT || attachment == LOCAL_GL_STENCIL_ATTACHMENT)){
|
||||
renderbuffername = mDepthStencilAttachment.Renderbuffer() ? mDepthStencilAttachment.Renderbuffer()->GLName() : 0;
|
||||
}
|
||||
mContext->gl->fFramebufferRenderbuffer(target, attachment, rbtarget, renderbuffername);
|
||||
}
|
||||
}
|
||||
|
@ -2918,11 +2928,21 @@ public:
|
|||
}
|
||||
|
||||
mContext->MakeContextCurrent();
|
||||
WebGLuint texturename = wtex ? wtex->GLName() : 0;
|
||||
WebGLuint paramtexturename = wtex ? wtex->GLName() : 0;
|
||||
if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
|
||||
mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_DEPTH_ATTACHMENT, textarget, texturename, level);
|
||||
mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_STENCIL_ATTACHMENT, textarget, texturename, level);
|
||||
WebGLuint depthtexturename = paramtexturename;
|
||||
WebGLuint stenciltexturename = paramtexturename;
|
||||
if(!paramtexturename){
|
||||
depthtexturename = mDepthAttachment.Texture() ? mDepthAttachment.Texture()->GLName() : 0;
|
||||
stenciltexturename = mStencilAttachment.Texture() ? mStencilAttachment.Texture()->GLName() : 0;
|
||||
}
|
||||
mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_DEPTH_ATTACHMENT, textarget, depthtexturename, level);
|
||||
mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_STENCIL_ATTACHMENT, textarget, stenciltexturename, level);
|
||||
} else {
|
||||
WebGLuint texturename = paramtexturename;
|
||||
if(!paramtexturename && (attachment == LOCAL_GL_DEPTH_ATTACHMENT || attachment == LOCAL_GL_STENCIL_ATTACHMENT)){
|
||||
texturename = mDepthStencilAttachment.Texture() ? mDepthStencilAttachment.Texture()->GLName() : 0;
|
||||
}
|
||||
mContext->gl->fFramebufferTexture2D(target, attachment, textarget, texturename, level);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "nsISeekableStream.h"
|
||||
#include "pratom.h"
|
||||
#include "nsMediaPluginReader.h"
|
||||
#include "nsIGfxInfo.h"
|
||||
|
||||
#include "MPAPI.h"
|
||||
|
||||
|
@ -87,6 +88,29 @@ static PluginHost sPluginHost = {
|
|||
|
||||
void nsMediaPluginHost::TryLoad(const char *name)
|
||||
{
|
||||
bool forceEnabled =
|
||||
Preferences::GetBool("stagefright.force-enabled", false);
|
||||
bool disabled =
|
||||
Preferences::GetBool("stagefright.disabled", false);
|
||||
|
||||
if (disabled) {
|
||||
NS_WARNING("XXX stagefright disabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!forceEnabled) {
|
||||
nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
|
||||
if (gfxInfo) {
|
||||
int32_t status;
|
||||
if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_STAGEFRIGHT, &status))) {
|
||||
if (status != nsIGfxInfo::FEATURE_NO_INFO) {
|
||||
NS_WARNING("XXX stagefright blacklisted\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRLibrary *lib = PR_LoadLibrary(name);
|
||||
if (lib) {
|
||||
Manifest *manifest = static_cast<Manifest *>(PR_FindSymbol(lib, "MPAPI_MANIFEST"));
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "AudioDestinationNode.h"
|
||||
#include "AudioBufferSourceNode.h"
|
||||
#include "AudioBuffer.h"
|
||||
#include "GainNode.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -86,6 +87,13 @@ AudioContext::CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels,
|
|||
return buffer.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<GainNode>
|
||||
AudioContext::CreateGain()
|
||||
{
|
||||
nsRefPtr<GainNode> gainNode = new GainNode(this);
|
||||
return gainNode.forget();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace dom {
|
|||
class AudioDestinationNode;
|
||||
class AudioBufferSourceNode;
|
||||
class AudioBuffer;
|
||||
class GainNode;
|
||||
|
||||
class AudioContext MOZ_FINAL : public nsWrapperCache,
|
||||
public EnableWebAudioCheck
|
||||
|
@ -61,6 +62,9 @@ public:
|
|||
uint32_t aLength, float aSampleRate,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<GainNode>
|
||||
CreateGain();
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIDOMWindow> mWindow;
|
||||
nsRefPtr<AudioDestinationNode> mDestination;
|
||||
|
|
|
@ -153,7 +153,19 @@ public:
|
|||
|
||||
void CancelScheduledValues(float aStartTime)
|
||||
{
|
||||
// TODO: implement
|
||||
for (unsigned i = 0; i < mEvents.Length(); ++i) {
|
||||
if (mEvents[i].mTime >= aStartTime) {
|
||||
#ifdef DEBUG
|
||||
// Sanity check: the array should be sorted, so all of the following
|
||||
// events should have a time greater than aStartTime too.
|
||||
for (unsigned j = i + 1; j < mEvents.Length(); ++j) {
|
||||
MOZ_ASSERT(mEvents[j].mTime >= aStartTime);
|
||||
}
|
||||
#endif
|
||||
mEvents.TruncateLength(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This method computes the AudioParam value at a given time based on the event timeline
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#include "GainNode.h"
|
||||
#include "mozilla/dom/GainNodeBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(GainNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(GainNode, AudioNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mGain)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(GainNode, AudioNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(tmp->mGain, AudioParam, "gain value")
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(GainNode)
|
||||
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(GainNode, AudioNode)
|
||||
NS_IMPL_RELEASE_INHERITED(GainNode, AudioNode)
|
||||
|
||||
GainNode::GainNode(AudioContext* aContext)
|
||||
: AudioNode(aContext)
|
||||
, mGain(new AudioParam(aContext, 1.0f, 0.0f, 1.0f))
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
GainNode::WrapObject(JSContext* aCx, JSObject* aScope,
|
||||
bool* aTriedToWrap)
|
||||
{
|
||||
return GainNodeBinding::Wrap(aCx, aScope, this, aTriedToWrap);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef GainNode_h_
|
||||
#define GainNode_h_
|
||||
|
||||
#include "AudioNode.h"
|
||||
#include "AudioParam.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
|
||||
class GainNode : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit GainNode(AudioContext* aContext);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GainNode, AudioNode)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,
|
||||
bool* aTriedToWrap);
|
||||
|
||||
virtual uint32_t MaxNumberOfInputs() const MOZ_FINAL MOZ_OVERRIDE
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual uint32_t MaxNumberOfOutputs() const MOZ_FINAL MOZ_OVERRIDE
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
AudioParam* Gain() const
|
||||
{
|
||||
return mGain;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<AudioParam> mGain;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -23,6 +23,7 @@ CPPSRCS := \
|
|||
AudioParam.cpp \
|
||||
AudioSourceNode.cpp \
|
||||
EnableWebAudioCheck.cpp \
|
||||
GainNode.cpp \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS_NAMESPACES := mozilla/dom
|
||||
|
@ -33,6 +34,7 @@ EXPORTS_mozilla/dom := \
|
|||
AudioNode.h \
|
||||
AudioParam.h \
|
||||
AudioSourceNode.h \
|
||||
GainNode.h \
|
||||
$(NULL)
|
||||
|
||||
PARALLEL_DIRS := test
|
||||
|
|
|
@ -208,6 +208,25 @@ void TestEventReplacement()
|
|||
is(timeline.GetValueAtTime(0.1f), 30.0f, "The first event should be overwritten");
|
||||
}
|
||||
|
||||
void TestEventRemoval()
|
||||
{
|
||||
Timeline timeline(10.0f, .1f, 20.0f);
|
||||
|
||||
ErrorResultMock rv;
|
||||
|
||||
timeline.SetValueAtTime(10.0f, 0.1f, rv);
|
||||
timeline.SetValueAtTime(15.0f, 0.15f, rv);
|
||||
timeline.SetValueAtTime(20.0f, 0.2f, rv);
|
||||
timeline.LinearRampToValueAtTime(30.0f, 0.3f, rv);
|
||||
is(timeline.GetEventCount(), 4, "Should have three events initially");
|
||||
timeline.CancelScheduledValues(0.4f);
|
||||
is(timeline.GetEventCount(), 4, "Trying to delete past the end of the array should have no effect");
|
||||
timeline.CancelScheduledValues(0.3f);
|
||||
is(timeline.GetEventCount(), 3, "Should successfully delete one event");
|
||||
timeline.CancelScheduledValues(0.12f);
|
||||
is(timeline.GetEventCount(), 1, "Should successfully delete two events");
|
||||
}
|
||||
|
||||
void TestBeforeFirstEvent()
|
||||
{
|
||||
Timeline timeline(10.0f, .1f, 20.0f);
|
||||
|
@ -327,6 +346,7 @@ int main()
|
|||
TestSpecExample();
|
||||
TestInvalidEvents();
|
||||
TestEventReplacement();
|
||||
TestEventRemoval();
|
||||
TestBeforeFirstEvent();
|
||||
TestAfterLastValueEvent();
|
||||
TestAfterLastTargetValueEvent();
|
||||
|
|
|
@ -14,6 +14,7 @@ MOCHITEST_FILES := \
|
|||
test_AudioBuffer.html \
|
||||
test_AudioContext.html \
|
||||
test_badConnect.html \
|
||||
test_gainNode.html \
|
||||
test_singleSourceDest.html \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test GainNode</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
SpecialPowers.setBoolPref("media.webaudio.enabled", true);
|
||||
|
||||
var context = new mozAudioContext();
|
||||
var buffer = context.createBuffer(1, 2048, 44100);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / 44100);
|
||||
}
|
||||
|
||||
var destination = context.destination;
|
||||
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var gain = context.createGain();
|
||||
|
||||
source.buffer = buffer;
|
||||
|
||||
source.connect(gain);
|
||||
gain.connect(destination);
|
||||
|
||||
ok(gain.gain, "The audioparam member must exist");
|
||||
is(gain.gain.value, 1.0, "Correct initial value");
|
||||
is(gain.gain.defaultValue, 1.0, "Correct default value");
|
||||
is(gain.gain.minValue, 0, "Correct min value");
|
||||
is(gain.gain.maxValue, 1.0, "Correct max value");
|
||||
gain.gain.value = 0.5;
|
||||
is(gain.gain.value, 0.5, "Correct initial value");
|
||||
is(gain.gain.defaultValue, 1.0, "Correct default value");
|
||||
is(gain.gain.minValue, 0, "Correct min value");
|
||||
is(gain.gain.maxValue, 1.0, "Correct max value");
|
||||
|
||||
source.start(0);
|
||||
SimpleTest.executeSoon(function() {
|
||||
source.stop(0);
|
||||
source.disconnect();
|
||||
gain.disconnect();
|
||||
|
||||
SpecialPowers.clearUserPref("media.webaudio.enabled");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -511,12 +511,11 @@ nsDOMWindowUtils::SendMouseEvent(const nsAString& aType,
|
|||
int32_t aModifiers,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
float aPressure,
|
||||
unsigned short aInputSourceArg,
|
||||
bool *aPreventDefault)
|
||||
unsigned short aInputSourceArg)
|
||||
{
|
||||
return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
|
||||
aIgnoreRootScrollFrame, aPressure,
|
||||
aInputSourceArg, false, aPreventDefault);
|
||||
aInputSourceArg, false);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -533,7 +532,7 @@ nsDOMWindowUtils::SendMouseEventToWindow(const nsAString& aType,
|
|||
SAMPLE_LABEL("nsDOMWindowUtils", "SendMouseEventToWindow");
|
||||
return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
|
||||
aIgnoreRootScrollFrame, aPressure,
|
||||
aInputSourceArg, true, nullptr);
|
||||
aInputSourceArg, true);
|
||||
}
|
||||
|
||||
static nsIntPoint
|
||||
|
@ -556,8 +555,7 @@ nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType,
|
|||
bool aIgnoreRootScrollFrame,
|
||||
float aPressure,
|
||||
unsigned short aInputSourceArg,
|
||||
bool aToWindow,
|
||||
bool *aPreventDefault)
|
||||
bool aToWindow)
|
||||
{
|
||||
if (!nsContentUtils::IsCallerChrome()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
|
@ -584,9 +582,7 @@ nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType,
|
|||
else if (aType.EqualsLiteral("contextmenu")) {
|
||||
msg = NS_CONTEXTMENU;
|
||||
contextMenuKey = (aButton == 0);
|
||||
} else if (aType.EqualsLiteral("MozMouseHittest"))
|
||||
msg = NS_MOUSE_MOZHITTEST;
|
||||
else
|
||||
} else
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (aInputSourceArg == nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN) {
|
||||
|
@ -627,10 +623,7 @@ nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType,
|
|||
status = nsEventStatus_eIgnore;
|
||||
return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
|
||||
}
|
||||
nsresult rv = widget->DispatchEvent(&event, status);
|
||||
*aPreventDefault = (status == nsEventStatus_eConsumeNoDefault);
|
||||
|
||||
return rv;
|
||||
return widget->DispatchEvent(&event, status);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -44,8 +44,7 @@ protected:
|
|||
bool aIgnoreRootScrollFrame,
|
||||
float aPressure,
|
||||
unsigned short aInputSourceArg,
|
||||
bool aToWindow,
|
||||
bool *aPreventDefault);
|
||||
bool aToWindow);
|
||||
|
||||
static mozilla::widget::Modifiers GetWidgetModifiers(int32_t aModifiers);
|
||||
};
|
||||
|
|
|
@ -1545,6 +1545,7 @@ nsJSContext::CompileScript(const PRUnichar* aText,
|
|||
nsScriptObjectHolder<JSScript>& aScriptObject,
|
||||
bool aSaveSource /* = false */)
|
||||
{
|
||||
SAMPLE_LABEL_PRINTF("JS", "Compile Script", "%s", aURL ? aURL : "");
|
||||
NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aPrincipal);
|
||||
|
|
|
@ -202,6 +202,11 @@ DOMInterfaces = {
|
|||
'workers': True,
|
||||
}],
|
||||
|
||||
'GainNode': [
|
||||
{
|
||||
'resultNotAddRefed': [ 'gain' ],
|
||||
}],
|
||||
|
||||
'HTMLCollection': [
|
||||
{
|
||||
'nativeType': 'nsIHTMLCollection',
|
||||
|
|
|
@ -2387,45 +2387,6 @@ BluetoothDBusService::Disconnect(const uint16_t aProfileId,
|
|||
DispatchBluetoothReply(aRunnable, v, replyError);
|
||||
}
|
||||
|
||||
class CreateBluetoothScoSocket : public nsRunnable
|
||||
{
|
||||
public:
|
||||
CreateBluetoothScoSocket(UnixSocketConsumer* aConsumer,
|
||||
const nsAString& aAddress,
|
||||
bool aAuth,
|
||||
bool aEncrypt)
|
||||
: mConsumer(aConsumer),
|
||||
mAddress(aAddress),
|
||||
mAuth(aAuth),
|
||||
mEncrypt(aEncrypt)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
Run()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
nsString replyError;
|
||||
BluetoothUnixSocketConnector* c =
|
||||
new BluetoothUnixSocketConnector(BluetoothSocketType::SCO, -1,
|
||||
mAuth, mEncrypt);
|
||||
|
||||
if (!mConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(mAddress).get())) {
|
||||
replyError.AssignLiteral("SocketConnectionError");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<UnixSocketConsumer> mConsumer;
|
||||
nsString mAddress;
|
||||
bool mAuth;
|
||||
bool mEncrypt;
|
||||
};
|
||||
|
||||
class ConnectBluetoothSocketRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
|
@ -2583,12 +2544,13 @@ BluetoothDBusService::GetScoSocket(const nsAString& aAddress,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsRefPtr<nsRunnable> func(new CreateBluetoothScoSocket(aConsumer,
|
||||
aAddress,
|
||||
aAuth,
|
||||
aEncrypt));
|
||||
if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Cannot dispatch firmware loading task!");
|
||||
nsString replyError;
|
||||
BluetoothUnixSocketConnector* c =
|
||||
new BluetoothUnixSocketConnector(BluetoothSocketType::SCO, -1,
|
||||
aAuth, aEncrypt);
|
||||
|
||||
if (!aConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(aAddress).get())) {
|
||||
replyError.AssignLiteral("SocketConnectionError");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ interface nsIDOMTouch;
|
|||
interface nsIDOMClientRect;
|
||||
interface nsIURI;
|
||||
|
||||
[scriptable, uuid(b3a3589d-cc9d-4123-9b21-51c66e88b436)]
|
||||
[scriptable, uuid(C98B7275-93C4-4EAD-B7CF-573D872C1071)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
|
@ -204,8 +204,7 @@ interface nsIDOMWindowUtils : nsISupports {
|
|||
const long MODIFIER_OS = 0x0400;
|
||||
|
||||
/** Synthesize a mouse event. The event types supported are:
|
||||
* mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu,
|
||||
* MozMouseHitTest
|
||||
* mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu
|
||||
*
|
||||
* Events are sent in coordinates offset by aX and aY from the window.
|
||||
*
|
||||
|
@ -237,18 +236,16 @@ interface nsIDOMWindowUtils : nsISupports {
|
|||
* @param aPressure touch input pressure: 0.0 -> 1.0
|
||||
* @param aInputSourceArg input source, see nsIDOMMouseEvent for values,
|
||||
* defaults to mouse input.
|
||||
*
|
||||
* returns true if the page called prevent default on this event
|
||||
*/
|
||||
boolean sendMouseEvent(in AString aType,
|
||||
in float aX,
|
||||
in float aY,
|
||||
in long aButton,
|
||||
in long aClickCount,
|
||||
in long aModifiers,
|
||||
[optional] in boolean aIgnoreRootScrollFrame,
|
||||
[optional] in float aPressure,
|
||||
[optional] in unsigned short aInputSourceArg);
|
||||
void sendMouseEvent(in AString aType,
|
||||
in float aX,
|
||||
in float aY,
|
||||
in long aButton,
|
||||
in long aClickCount,
|
||||
in long aModifiers,
|
||||
[optional] in boolean aIgnoreRootScrollFrame,
|
||||
[optional] in float aPressure,
|
||||
[optional] in unsigned short aInputSourceArg);
|
||||
|
||||
/** Synthesize a touch event. The event types supported are:
|
||||
* touchstart, touchend, touchmove, and touchcancel
|
||||
|
|
|
@ -1276,9 +1276,8 @@ TabChild::RecvMouseEvent(const nsString& aType,
|
|||
{
|
||||
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
|
||||
NS_ENSURE_TRUE(utils, true);
|
||||
bool ignored = false;
|
||||
utils->SendMouseEvent(aType, aX, aY, aButton, aClickCount, aModifiers,
|
||||
aIgnoreRootScrollFrame, 0, 0, &ignored);
|
||||
aIgnoreRootScrollFrame, 0, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,8 @@ SettingsLock.prototype = {
|
|||
var lock;
|
||||
while (lock = this._settingsManager._locks.dequeue()) {
|
||||
if (!lock._transaction) {
|
||||
lock._transaction = lock._settingsManager._settingsDB._db.transaction(SETTINGSSTORE_NAME, "readwrite");
|
||||
let transactionType = this._settingsManager.hasWritePrivileges ? "readwrite" : "readonly";
|
||||
lock._transaction = lock._settingsManager._settingsDB._db.transaction(SETTINGSSTORE_NAME, transactionType);
|
||||
}
|
||||
lock.process();
|
||||
}
|
||||
|
|
|
@ -11,4 +11,3 @@ qemu = true
|
|||
[test_outgoing_delete.js]
|
||||
[test_getmessage.js]
|
||||
[test_getmessage_notfound.js]
|
||||
[test_timestamp.js]
|
||||
|
|
|
@ -1,189 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 15000;
|
||||
|
||||
SpecialPowers.setBoolPref("dom.sms.enabled", true);
|
||||
SpecialPowers.addPermission("sms", true, document);
|
||||
|
||||
let sms = window.navigator.mozSms;
|
||||
let inText;
|
||||
let remoteNumber = "5559997777";
|
||||
let outText;
|
||||
let gotSmsOnsent;
|
||||
let gotReqOnsuccess;
|
||||
let inSmsId = 0;
|
||||
let outSmsId = 0;
|
||||
let inSmsTime = 0;
|
||||
let outSmsTime = 0;
|
||||
let testCount = 10;
|
||||
|
||||
function verifyInitialState() {
|
||||
log("Verifying initial state.");
|
||||
ok(sms, "mozSms");
|
||||
simulateIncomingSms();
|
||||
}
|
||||
|
||||
function simulateIncomingSms() {
|
||||
log("Simulating incoming SMS.");
|
||||
|
||||
sms.onreceived = function onreceived(event) {
|
||||
log("Received 'onreceived' smsmanager event.");
|
||||
let incomingSms = event.message;
|
||||
ok(incomingSms, "incoming sms");
|
||||
ok(incomingSms.id, "sms id");
|
||||
inSmsId = incomingSms.id;
|
||||
is(incomingSms.body, inText, "msg body");
|
||||
is(incomingSms.delivery, "received", "delivery");
|
||||
is(incomingSms.read, false, "read");
|
||||
is(incomingSms.receiver, null, "receiver");
|
||||
is(incomingSms.sender, remoteNumber, "sender");
|
||||
is(incomingSms.messageClass, "normal", "messageClass");
|
||||
ok(incomingSms.timestamp instanceof Date, "timestamp is instanceof date");
|
||||
// The max resolution of the SCTS (SMS Centre TimeStamp) is one second
|
||||
// therefore we want to compare in seconds not milliseconds. No need to
|
||||
// round received sms timestamp, the value is in milliseconds but at
|
||||
// seconds resolution already (always ends with 000) so just convert
|
||||
inSmsTime = Math.floor(incomingSms.timestamp.getTime() / 1000);
|
||||
log("Received SMS (id: " + inSmsId + ") timestamp: " + inSmsTime + ".");
|
||||
if(outSmsTime) {
|
||||
// Test is repeating; compare received SMS timestamp with last sent sms.
|
||||
// Can be some time drift between the SCTS and host/emulator, but we are
|
||||
// comparing seconds (not milliseconds) so should be ok. If this test
|
||||
// goes random orange, then may need to add a 1 second lee-way here.
|
||||
if(inSmsTime >= outSmsTime) {
|
||||
log("Timestamp in sms " + inSmsId + " is >= timestamp in sms "
|
||||
+ outSmsId + ".");
|
||||
} else {
|
||||
log("* Timestamp in sms " + inSmsId + " is < timestamp in sms "
|
||||
+ outSmsId + ".");
|
||||
ok(false, "sms timestamp is incorrect");
|
||||
}
|
||||
}
|
||||
sendSms();
|
||||
};
|
||||
// Simulate incoming sms sent from remoteNumber to our emulator
|
||||
inText = "Incoming SMS " + Date.now();
|
||||
runEmulatorCmd("sms send " + remoteNumber + " " + inText, function(result) {
|
||||
is(result[0], "OK", "emulator output");
|
||||
});
|
||||
}
|
||||
|
||||
function sendSms() {
|
||||
log("Sending an SMS.");
|
||||
let gotSmsOnsent = false;
|
||||
let gotReqOnsuccess = false;
|
||||
sms.onsent = function(event) {
|
||||
log("Received 'onsent' smsmanager event.");
|
||||
gotSmsOnsent = true;
|
||||
let sentSms = event.message;
|
||||
ok(sentSms, "outgoing sms");
|
||||
ok(sentSms.id, "sms id");
|
||||
outSmsId = sentSms.id;
|
||||
is(sentSms.body, outText, "msg body");
|
||||
is(sentSms.delivery, "sent", "delivery");
|
||||
is(sentSms.read, true, "read");
|
||||
is(sentSms.receiver, remoteNumber, "receiver");
|
||||
is(sentSms.sender, null, "sender");
|
||||
is(sentSms.messageClass, "normal", "messageClass");
|
||||
ok(sentSms.timestamp instanceof Date, "timestamp is instanceof date");
|
||||
// The max resolution of the SCTS (SMS Centre TimeStamp) is one second
|
||||
// therefore we want to compare in seconds not milliseconds. Round the
|
||||
// sent sms timestamp to nearest second.
|
||||
outSmsTime = Math.round(sentSms.timestamp.getTime() / 1000);
|
||||
log("Sent SMS (id: " + outSmsId + ") timestamp: " + outSmsTime + ".");
|
||||
|
||||
if (gotSmsOnsent && gotReqOnsuccess) { verifyTimeStamps(); }
|
||||
};
|
||||
outText = "Outgoing SMS " + Date.now();
|
||||
let requestRet = sms.send(remoteNumber, outText);
|
||||
ok(requestRet, "smsrequest obj returned");
|
||||
|
||||
requestRet.onsuccess = function(event) {
|
||||
log("Received 'onsuccess' smsrequest event.");
|
||||
gotReqOnsuccess = true;
|
||||
if(event.target.result){
|
||||
if (gotSmsOnsent && gotReqOnsuccess) { verifyTimeStamps(); }
|
||||
} else {
|
||||
log("smsrequest returned false for sms.send");
|
||||
ok(false,"SMS send failed");
|
||||
cleanUp();
|
||||
}
|
||||
};
|
||||
|
||||
requestRet.onerror = function(event) {
|
||||
log("Received 'onerror' smsrequest event.");
|
||||
ok(event.target.error, "domerror obj");
|
||||
ok(false, "sms.send request returned unexpected error: "
|
||||
+ event.target.error.name );
|
||||
cleanUp();
|
||||
};
|
||||
}
|
||||
|
||||
function verifyTimeStamps() {
|
||||
// Compare sent sms timestamp with that of the previously received sms.
|
||||
// Can be some time drift between the SCTS and host/emulator, but we are
|
||||
// comparing seconds (not milliseconds) so should be ok. If this test
|
||||
// goes random orange, then may need to add a 1 second lee-way here.
|
||||
if(outSmsTime >= inSmsTime) {
|
||||
log("Timestamp in sms " + outSmsId + " is >= timestamp in sms "
|
||||
+ inSmsId + ".");
|
||||
} else {
|
||||
log("* Timestamp in sms " + outSmsId + " is < timestamp in sms "
|
||||
+ inSmsId + ".");
|
||||
ok(false, "sms timestamp is incorrect");
|
||||
}
|
||||
deleteMsgs();
|
||||
}
|
||||
|
||||
function deleteMsgs() {
|
||||
log("Deleting SMS (id: " + inSmsId + ").");
|
||||
let requestRet = sms.delete(inSmsId);
|
||||
ok(requestRet,"smsrequest obj returned");
|
||||
|
||||
requestRet.onsuccess = function(event) {
|
||||
log("Received 'onsuccess' smsrequest event.");
|
||||
if(event.target.result){
|
||||
log("Deleting SMS (id: " + outSmsId + ").");
|
||||
let nextReqRet = sms.delete(outSmsId);
|
||||
ok(nextReqRet,"smsrequest obj returned");
|
||||
|
||||
nextReqRet.onsuccess = function(event) {
|
||||
log("Received 'onsuccess' smsrequest event.");
|
||||
if(event.target.result) {
|
||||
if(--testCount) {
|
||||
simulateIncomingSms();
|
||||
} else {
|
||||
cleanUp();
|
||||
}
|
||||
} else {
|
||||
log("smsrequest returned false for sms.delete");
|
||||
ok(false,"SMS delete failed");
|
||||
cleanUp();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
log("smsrequest returned false for sms.delete");
|
||||
ok(false,"SMS delete failed");
|
||||
cleanUp();
|
||||
}
|
||||
};
|
||||
|
||||
requestRet.onerror = function(event) {
|
||||
log("Received 'onerror' smsrequest event.");
|
||||
ok(event.target.error, "domerror obj");
|
||||
ok(false, "sms.delete request returned unexpected error: "
|
||||
+ event.target.error.name );
|
||||
cleanUp();
|
||||
};
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
sms.onreceived = null;
|
||||
SpecialPowers.removePermission("sms", document);
|
||||
SpecialPowers.setBoolPref("dom.sms.enabled", false);
|
||||
finish();
|
||||
}
|
||||
|
||||
// Start the test
|
||||
verifyInitialState();
|
|
@ -25,6 +25,9 @@ interface mozAudioContext {
|
|||
[Creator]
|
||||
AudioBufferSourceNode createBufferSource();
|
||||
|
||||
[Creator]
|
||||
GainNode createGain();
|
||||
|
||||
};
|
||||
|
||||
typedef mozAudioContext AudioContext;
|
||||
|
|
|
@ -37,7 +37,7 @@ interface AudioParam {
|
|||
// void setValueCurveAtTime(Float32Array values, float startTime, float duration);
|
||||
|
||||
// Cancels all scheduled parameter changes with times greater than or equal to startTime.
|
||||
// void cancelScheduledValues(float startTime);
|
||||
void cancelScheduledValues(float startTime);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
|
||||
*
|
||||
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[PrefControlled]
|
||||
interface GainNode : AudioNode {
|
||||
|
||||
readonly attribute AudioParam gain;
|
||||
|
||||
};
|
||||
|
|
@ -29,6 +29,7 @@ webidl_files = \
|
|||
EventTarget.webidl \
|
||||
FileList.webidl \
|
||||
FileReaderSync.webidl \
|
||||
GainNode.webidl \
|
||||
HTMLCollection.webidl \
|
||||
HTMLOptionsCollection.webidl \
|
||||
HTMLPropertiesCollection.webidl \
|
||||
|
|
|
@ -10,6 +10,7 @@ extern "C" {
|
|||
#include <libgnomevfs/gnome-vfs-mime-utils.h>
|
||||
}
|
||||
|
||||
#include "NSPRFormatTime.h" // must be before anything that includes prtime.h
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
|
|
|
@ -921,7 +921,7 @@ void ExtractFontsFromJar(nsIFile* aLocalDir)
|
|||
jarFile->GetLastModifiedTime(&jarModifiedTime);
|
||||
|
||||
mozilla::scache::StartupCache* cache = mozilla::scache::StartupCache::GetSingleton();
|
||||
if (NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME, &cachedModifiedTimeBuf, &longSize))
|
||||
if (cache && NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME, &cachedModifiedTimeBuf, &longSize))
|
||||
&& longSize == sizeof(int64_t)) {
|
||||
if (jarModifiedTime < *((int64_t*) cachedModifiedTimeBuf)) {
|
||||
return;
|
||||
|
@ -979,7 +979,7 @@ void ExtractFontsFromJar(nsIFile* aLocalDir)
|
|||
}
|
||||
}
|
||||
}
|
||||
if (allFontsExtracted) {
|
||||
if (allFontsExtracted && cache) {
|
||||
cache->PutBuffer(JAR_LAST_MODIFED_TIME, (char*)&jarModifiedTime, sizeof(int64_t));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -703,17 +703,21 @@ ifdef relativesrcdir
|
|||
LOCALE_SRCDIR = $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir))
|
||||
endif
|
||||
|
||||
ifdef LOCALE_SRCDIR
|
||||
# if LOCALE_MERGEDIR is set, use mergedir first, then the localization,
|
||||
# and finally en-US
|
||||
ifdef relativesrcdir
|
||||
MAKE_JARS_FLAGS += --relativesrcdir=$(relativesrcdir)
|
||||
ifneq (en-US,$(AB_CD))
|
||||
ifdef LOCALE_MERGEDIR
|
||||
MAKE_JARS_FLAGS += -c $(LOCALE_MERGEDIR)/$(subst /locales,,$(relativesrcdir))
|
||||
MAKE_JARS_FLAGS += --locale-mergedir=$(LOCALE_MERGEDIR)
|
||||
endif
|
||||
ifdef IS_LANGUAGE_REPACK
|
||||
MAKE_JARS_FLAGS += --l10n-base=$(L10NBASEDIR)
|
||||
endif
|
||||
else
|
||||
MAKE_JARS_FLAGS += -c $(LOCALE_SRCDIR)
|
||||
ifdef LOCALE_MERGEDIR
|
||||
MAKE_JARS_FLAGS += -c $(topsrcdir)/$(relativesrcdir)/en-US
|
||||
endif
|
||||
endif
|
||||
endif # en-US
|
||||
else
|
||||
MAKE_JARS_FLAGS += -c $(LOCALE_SRCDIR)
|
||||
endif # ! relativesrcdir
|
||||
|
||||
ifdef LOCALE_MERGEDIR
|
||||
MERGE_FILE = $(firstword \
|
||||
|
|
|
@ -48,6 +48,7 @@ JSCompartment::JSCompartment(JSRuntime *rt)
|
|||
gcStoreBuffer(&gcNursery),
|
||||
#endif
|
||||
needsBarrier_(false),
|
||||
ionUsingBarriers_(false),
|
||||
gcScheduled(false),
|
||||
gcState(NoGC),
|
||||
gcPreserveCode(false),
|
||||
|
@ -131,7 +132,7 @@ JSCompartment::init(JSContext *cx)
|
|||
}
|
||||
|
||||
void
|
||||
JSCompartment::setNeedsBarrier(bool needs)
|
||||
JSCompartment::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon)
|
||||
{
|
||||
#ifdef JS_METHODJIT
|
||||
/* ClearAllFrames calls compileBarriers() and needs the old value. */
|
||||
|
@ -141,8 +142,10 @@ JSCompartment::setNeedsBarrier(bool needs)
|
|||
#endif
|
||||
|
||||
#ifdef JS_ION
|
||||
if (needsBarrier_ != needs)
|
||||
if (updateIon == UpdateIon && needs != ionUsingBarriers_) {
|
||||
ion::ToggleBarriers(this, needs);
|
||||
ionUsingBarriers_ = needs;
|
||||
}
|
||||
#endif
|
||||
|
||||
needsBarrier_ = needs;
|
||||
|
|
|
@ -155,6 +155,7 @@ struct JSCompartment
|
|||
|
||||
private:
|
||||
bool needsBarrier_;
|
||||
bool ionUsingBarriers_;
|
||||
public:
|
||||
|
||||
bool needsBarrier() const {
|
||||
|
@ -169,7 +170,12 @@ struct JSCompartment
|
|||
return compileBarriers(needsBarrier());
|
||||
}
|
||||
|
||||
void setNeedsBarrier(bool needs);
|
||||
enum ShouldUpdateIon {
|
||||
DontUpdateIon,
|
||||
UpdateIon
|
||||
};
|
||||
|
||||
void setNeedsBarrier(bool needs, ShouldUpdateIon updateIon);
|
||||
|
||||
static size_t OffsetOfNeedsBarrier() {
|
||||
return offsetof(JSCompartment, needsBarrier_);
|
||||
|
|
|
@ -4079,7 +4079,7 @@ ResetIncrementalGC(JSRuntime *rt, const char *reason)
|
|||
AutoCopyFreeListToArenas copy(rt);
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
if (c->isGCMarking()) {
|
||||
c->setNeedsBarrier(false);
|
||||
c->setNeedsBarrier(false, JSCompartment::DontUpdateIon);
|
||||
c->setGCState(JSCompartment::NoGC);
|
||||
wasMarking = true;
|
||||
}
|
||||
|
@ -4137,10 +4137,15 @@ AutoGCSlice::AutoGCSlice(JSRuntime *rt)
|
|||
rt->stackSpace.markActiveCompartments();
|
||||
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
/* Clear this early so we don't do any write barriers during GC. */
|
||||
/*
|
||||
* Clear needsBarrier early so we don't do any write barriers during
|
||||
* GC. We don't need to update the Ion barriers (which is expensive)
|
||||
* because Ion code doesn't run during GC. If need be, we'll update the
|
||||
* Ion barriers in ~AutoGCSlice.
|
||||
*/
|
||||
if (c->isGCMarking()) {
|
||||
JS_ASSERT(c->needsBarrier());
|
||||
c->setNeedsBarrier(false);
|
||||
c->setNeedsBarrier(false, JSCompartment::DontUpdateIon);
|
||||
} else {
|
||||
JS_ASSERT(!c->needsBarrier());
|
||||
}
|
||||
|
@ -4151,11 +4156,11 @@ AutoGCSlice::~AutoGCSlice()
|
|||
{
|
||||
for (GCCompartmentsIter c(runtime); !c.done(); c.next()) {
|
||||
if (c->isGCMarking()) {
|
||||
c->setNeedsBarrier(true);
|
||||
c->setNeedsBarrier(true, JSCompartment::UpdateIon);
|
||||
c->arenas.prepareForIncrementalGC(runtime);
|
||||
} else {
|
||||
JS_ASSERT(c->isGCSweeping());
|
||||
c->setNeedsBarrier(false);
|
||||
c->setNeedsBarrier(false, JSCompartment::UpdateIon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4312,7 +4317,7 @@ IncrementalCollectSlice(JSRuntime *rt,
|
|||
|
||||
default:
|
||||
JS_ASSERT(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IncrementalSafety
|
||||
|
@ -5267,7 +5272,7 @@ StartVerifyPreBarriers(JSRuntime *rt)
|
|||
rt->gcMarker.start(rt);
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
PurgeJITCaches(c);
|
||||
c->setNeedsBarrier(true);
|
||||
c->setNeedsBarrier(true, JSCompartment::UpdateIon);
|
||||
c->arenas.purge();
|
||||
}
|
||||
|
||||
|
@ -5350,7 +5355,7 @@ EndVerifyPreBarriers(JSRuntime *rt)
|
|||
compartmentCreated = true;
|
||||
|
||||
PurgeJITCaches(c);
|
||||
c->setNeedsBarrier(false);
|
||||
c->setNeedsBarrier(false, JSCompartment::UpdateIon);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -7,8 +7,10 @@ package org.mozilla.gecko;
|
|||
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.db.BrowserDB.URLColumns;
|
||||
import org.mozilla.gecko.db.BrowserContract.Images;
|
||||
import org.mozilla.gecko.sync.setup.SyncAccounts;
|
||||
import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity;
|
||||
import org.mozilla.gecko.util.GeckoAsyncTask;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
@ -59,8 +61,10 @@ import java.io.InputStream;
|
|||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
@ -263,9 +267,8 @@ public class AboutHomeContent extends ScrollView
|
|||
mTopSitesAdapter = new TopSitesCursorAdapter(mActivity,
|
||||
R.layout.abouthome_topsite_item,
|
||||
mCursor,
|
||||
new String[] { URLColumns.TITLE,
|
||||
URLColumns.THUMBNAIL },
|
||||
new int[] { R.id.title, R.id.thumbnail });
|
||||
new String[] { URLColumns.TITLE },
|
||||
new int[] { R.id.title });
|
||||
|
||||
mTopSitesAdapter.setViewBinder(new TopSitesViewBinder());
|
||||
mTopSitesGrid.setAdapter(mTopSitesAdapter);
|
||||
|
@ -273,6 +276,9 @@ public class AboutHomeContent extends ScrollView
|
|||
mTopSitesAdapter.changeCursor(mCursor);
|
||||
}
|
||||
|
||||
if (mTopSitesAdapter.getCount() > 0)
|
||||
loadTopSitesThumbnails(resolver);
|
||||
|
||||
updateLayout(syncIsSetup);
|
||||
|
||||
// Free the old Cursor in the right thread now.
|
||||
|
@ -288,6 +294,97 @@ public class AboutHomeContent extends ScrollView
|
|||
});
|
||||
}
|
||||
|
||||
private List<String> getTopSitesUrls() {
|
||||
List<String> urls = new ArrayList<String>();
|
||||
|
||||
Cursor c = mTopSitesAdapter.getCursor();
|
||||
if (c == null || !c.moveToFirst())
|
||||
return urls;
|
||||
|
||||
do {
|
||||
final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
|
||||
urls.add(url);
|
||||
} while (c.moveToNext());
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
private void displayThumbnail(View view, Bitmap thumbnail) {
|
||||
ImageView thumbnailView = (ImageView) view.findViewById(R.id.thumbnail);
|
||||
|
||||
if (thumbnail == null) {
|
||||
thumbnailView.setImageResource(R.drawable.abouthome_thumbnail_bg);
|
||||
thumbnailView.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
} else {
|
||||
try {
|
||||
thumbnailView.setImageBitmap(thumbnail);
|
||||
thumbnailView.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
} catch (OutOfMemoryError oom) {
|
||||
Log.e(LOGTAG, "Unable to load thumbnail bitmap", oom);
|
||||
thumbnailView.setImageResource(R.drawable.abouthome_thumbnail_bg);
|
||||
thumbnailView.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTopSitesThumbnails(Map<String, Bitmap> thumbnails) {
|
||||
for (int i = 0; i < mTopSitesGrid.getChildCount(); i++) {
|
||||
final View view = mTopSitesGrid.getChildAt(i);
|
||||
|
||||
Cursor c = (Cursor) mTopSitesGrid.getItemAtPosition(i);
|
||||
final String url = c.getString(c.getColumnIndex(URLColumns.URL));
|
||||
|
||||
displayThumbnail(view, thumbnails.get(url));
|
||||
}
|
||||
|
||||
mTopSitesGrid.invalidate();
|
||||
}
|
||||
|
||||
public Map<String, Bitmap> getTopSitesThumbnails(Cursor c) {
|
||||
Map<String, Bitmap> thumbnails = new HashMap<String, Bitmap>();
|
||||
|
||||
try {
|
||||
if (c == null || !c.moveToFirst())
|
||||
return thumbnails;
|
||||
|
||||
do {
|
||||
final String url = c.getString(c.getColumnIndexOrThrow(Images.URL));
|
||||
final byte[] b = c.getBlob(c.getColumnIndexOrThrow(Images.THUMBNAIL));
|
||||
if (b == null)
|
||||
continue;
|
||||
|
||||
Bitmap thumbnail = BitmapFactory.decodeByteArray(b, 0, b.length);
|
||||
if (thumbnail == null)
|
||||
continue;
|
||||
|
||||
thumbnails.put(url, thumbnail);
|
||||
} while (c.moveToNext());
|
||||
} finally {
|
||||
if (c != null)
|
||||
c.close();
|
||||
}
|
||||
|
||||
return thumbnails;
|
||||
}
|
||||
|
||||
private void loadTopSitesThumbnails(final ContentResolver cr) {
|
||||
final List<String> urls = getTopSitesUrls();
|
||||
if (urls.size() == 0)
|
||||
return;
|
||||
|
||||
(new GeckoAsyncTask<Void, Void, Cursor>(GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
|
||||
@Override
|
||||
public Cursor doInBackground(Void... params) {
|
||||
return BrowserDB.getThumbnailsForUrls(cr, urls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostExecute(Cursor c) {
|
||||
updateTopSitesThumbnails(getTopSitesThumbnails(c));
|
||||
}
|
||||
}).execute();
|
||||
}
|
||||
|
||||
void update(final EnumSet<UpdateFlags> flags) {
|
||||
GeckoAppShell.getHandler().post(new Runnable() {
|
||||
public void run() {
|
||||
|
@ -675,28 +772,6 @@ public class AboutHomeContent extends ScrollView
|
|||
}
|
||||
|
||||
class TopSitesViewBinder implements SimpleCursorAdapter.ViewBinder {
|
||||
private boolean updateThumbnail(View view, Cursor cursor, int thumbIndex) {
|
||||
byte[] b = cursor.getBlob(thumbIndex);
|
||||
ImageView thumbnail = (ImageView) view;
|
||||
|
||||
if (b == null) {
|
||||
thumbnail.setImageResource(R.drawable.abouthome_thumbnail_bg);
|
||||
thumbnail.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
} else {
|
||||
try {
|
||||
Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
|
||||
thumbnail.setImageBitmap(bitmap);
|
||||
thumbnail.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
} catch (OutOfMemoryError oom) {
|
||||
Log.e(LOGTAG, "Unable to load thumbnail bitmap", oom);
|
||||
thumbnail.setImageResource(R.drawable.abouthome_thumbnail_bg);
|
||||
thumbnail.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean updateTitle(View view, Cursor cursor, int titleIndex) {
|
||||
String title = cursor.getString(titleIndex);
|
||||
TextView titleView = (TextView) view;
|
||||
|
@ -719,11 +794,6 @@ public class AboutHomeContent extends ScrollView
|
|||
return updateTitle(view, cursor, titleIndex);
|
||||
}
|
||||
|
||||
int thumbIndex = cursor.getColumnIndexOrThrow(URLColumns.THUMBNAIL);
|
||||
if (columnIndex == thumbIndex) {
|
||||
return updateThumbnail(view, cursor, thumbIndex);
|
||||
}
|
||||
|
||||
// Other columns are handled automatically
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.mozilla.gecko;
|
|||
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.util.GeckoJarReader;
|
||||
import org.mozilla.gecko.util.LruCache;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
|
@ -19,6 +20,7 @@ import android.database.Cursor;
|
|||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.http.AndroidHttpClient;
|
||||
|
@ -46,6 +48,7 @@ public class Favicons {
|
|||
|
||||
private Map<Long,LoadFaviconTask> mLoadTasks;
|
||||
private long mNextFaviconLoadId;
|
||||
private LruCache<String, Drawable> mFaviconsCache;
|
||||
private static final String USER_AGENT = GeckoApp.mAppContext.getDefaultUAString();
|
||||
private AndroidHttpClient mHttpClient;
|
||||
|
||||
|
@ -139,6 +142,15 @@ public class Favicons {
|
|||
|
||||
mLoadTasks = Collections.synchronizedMap(new HashMap<Long,LoadFaviconTask>());
|
||||
mNextFaviconLoadId = 0;
|
||||
|
||||
// Create a favicon memory cache that have up to 1mb of size
|
||||
mFaviconsCache = new LruCache<String, Drawable>(1024 * 1024) {
|
||||
@Override
|
||||
protected int sizeOf(String url, Drawable image) {
|
||||
Bitmap bitmap = ((BitmapDrawable) image).getBitmap();
|
||||
return bitmap.getRowBytes() * bitmap.getHeight();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private synchronized AndroidHttpClient getHttpClient() {
|
||||
|
@ -149,6 +161,20 @@ public class Favicons {
|
|||
return mHttpClient;
|
||||
}
|
||||
|
||||
private void dispatchResult(final String pageUrl, final Drawable image,
|
||||
final OnFaviconLoadedListener listener) {
|
||||
if (pageUrl != null && image != null)
|
||||
putFaviconInMemCache(pageUrl, image);
|
||||
|
||||
// We want to always run the listener on UI thread
|
||||
GeckoAppShell.getMainHandler().post(new Runnable() {
|
||||
public void run() {
|
||||
if (listener != null)
|
||||
listener.onFaviconLoaded(pageUrl, image);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public String getFaviconUrlForPageUrl(String pageUrl) {
|
||||
return mDbHelper.getFaviconUrlForPageUrl(pageUrl);
|
||||
}
|
||||
|
@ -158,8 +184,15 @@ public class Favicons {
|
|||
|
||||
// Handle the case where page url is empty
|
||||
if (pageUrl == null || pageUrl.length() == 0) {
|
||||
if (listener != null)
|
||||
listener.onFaviconLoaded(null, null);
|
||||
dispatchResult(null, null, listener);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if favicon is mem cached
|
||||
Drawable image = getFaviconFromMemCache(pageUrl);
|
||||
if (image != null) {
|
||||
dispatchResult(pageUrl, image, listener);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LoadFaviconTask task = new LoadFaviconTask(pageUrl, faviconUrl, persist, listener);
|
||||
|
@ -172,6 +205,18 @@ public class Favicons {
|
|||
return taskId;
|
||||
}
|
||||
|
||||
public Drawable getFaviconFromMemCache(String pageUrl) {
|
||||
return mFaviconsCache.get(pageUrl);
|
||||
}
|
||||
|
||||
public void putFaviconInMemCache(String pageUrl, Drawable image) {
|
||||
mFaviconsCache.put(pageUrl, image);
|
||||
}
|
||||
|
||||
public void clearMemCache() {
|
||||
mFaviconsCache.evictAll();
|
||||
}
|
||||
|
||||
public boolean cancelFaviconLoad(long taskId) {
|
||||
Log.d(LOGTAG, "Requesting cancelation of favicon load (" + taskId + ")");
|
||||
|
||||
|
@ -255,7 +300,7 @@ public class Favicons {
|
|||
// Runs in background thread
|
||||
private BitmapDrawable downloadFavicon(URL faviconUrl) {
|
||||
if (mFaviconUrl.startsWith("jar:jar:")) {
|
||||
return GeckoJarReader.getBitmapDrawable(GeckoApp.mAppContext.getResources(), mFaviconUrl);
|
||||
return GeckoJarReader.getBitmapDrawable(mContext.getResources(), mFaviconUrl);
|
||||
}
|
||||
|
||||
URI uri;
|
||||
|
@ -339,15 +384,7 @@ public class Favicons {
|
|||
@Override
|
||||
protected void onPostExecute(final BitmapDrawable image) {
|
||||
mLoadTasks.remove(mId);
|
||||
|
||||
if (mListener != null) {
|
||||
// We want to always run the listener on UI thread
|
||||
GeckoApp.mAppContext.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
mListener.onFaviconLoaded(mPageUrl, image);
|
||||
}
|
||||
});
|
||||
}
|
||||
dispatchResult(mPageUrl, image, mListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.mozilla.gecko.gfx.BitmapUtils;
|
|||
import org.mozilla.gecko.gfx.GeckoLayerClient;
|
||||
import org.mozilla.gecko.gfx.GfxInfoThread;
|
||||
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
|
||||
import org.mozilla.gecko.gfx.InputConnectionHandler;
|
||||
import org.mozilla.gecko.gfx.LayerView;
|
||||
import org.mozilla.gecko.gfx.TouchEventHandler;
|
||||
import org.mozilla.gecko.util.EventDispatcher;
|
||||
|
@ -109,7 +110,7 @@ public class GeckoAppShell
|
|||
|
||||
static private boolean gRestartScheduled = false;
|
||||
|
||||
static private GeckoInputConnection mInputConnection = null;
|
||||
static private GeckoEditableListener mEditableListener = null;
|
||||
|
||||
static private final HashMap<Integer, AlertNotification>
|
||||
mAlertNotifications = new HashMap<Integer, AlertNotification>();
|
||||
|
@ -534,8 +535,11 @@ public class GeckoAppShell
|
|||
// Called on the UI thread after Gecko loads.
|
||||
private static void geckoLoaded() {
|
||||
LayerView v = GeckoApp.mAppContext.getLayerView();
|
||||
mInputConnection = GeckoInputConnection.create(v);
|
||||
v.setInputConnectionHandler(mInputConnection);
|
||||
GeckoEditable editable = new GeckoEditable();
|
||||
InputConnectionHandler ich = GeckoInputConnection.create(v, editable);
|
||||
v.setInputConnectionHandler(ich);
|
||||
// install the gecko => editable listener
|
||||
mEditableListener = editable;
|
||||
}
|
||||
|
||||
static void sendPendingEventsToGecko() {
|
||||
|
@ -568,21 +572,30 @@ public class GeckoAppShell
|
|||
* The Gecko-side API: API methods that Gecko calls
|
||||
*/
|
||||
public static void notifyIME(int type, int state) {
|
||||
if (mInputConnection != null)
|
||||
mInputConnection.notifyIME(type, state);
|
||||
if (mEditableListener != null) {
|
||||
mEditableListener.notifyIME(type, state);
|
||||
}
|
||||
}
|
||||
|
||||
public static void notifyIMEEnabled(int state, String typeHint, String modeHint,
|
||||
String actionHint, boolean landscapeFS) {
|
||||
// notifyIMEEnabled() still needs the landscapeFS parameter because it is called from JNI
|
||||
// code that assumes it has the same signature as XUL Fennec's (which does use landscapeFS).
|
||||
if (mInputConnection != null)
|
||||
mInputConnection.notifyIMEEnabled(state, typeHint, modeHint, actionHint);
|
||||
public static void notifyIMEEnabled(int state, String typeHint,
|
||||
String modeHint, String actionHint,
|
||||
boolean landscapeFS) {
|
||||
// notifyIMEEnabled() still needs the landscapeFS parameter
|
||||
// because it is called from JNI code that assumes it has the
|
||||
// same signature as XUL Fennec's (which does use landscapeFS).
|
||||
// Bug 807124 will eliminate the need for landscapeFS
|
||||
if (mEditableListener != null) {
|
||||
mEditableListener.notifyIMEEnabled(state, typeHint,
|
||||
modeHint, actionHint);
|
||||
}
|
||||
}
|
||||
|
||||
public static void notifyIMEChange(String text, int start, int end, int newEnd) {
|
||||
if (mInputConnection != null)
|
||||
mInputConnection.notifyIMEChange(text, start, end, newEnd);
|
||||
if (newEnd < 0) { // Selection change
|
||||
mEditableListener.onSelectionChange(start, end);
|
||||
} else { // Text change
|
||||
mEditableListener.onTextChange(text, start, end, newEnd);
|
||||
}
|
||||
}
|
||||
|
||||
private static CountDownLatch sGeckoPendingAcks = null;
|
||||
|
@ -1997,8 +2010,10 @@ public class GeckoAppShell
|
|||
}
|
||||
|
||||
public static void viewSizeChanged() {
|
||||
if (mInputConnection != null && mInputConnection.isIMEEnabled()) {
|
||||
sendEventToGecko(GeckoEvent.createBroadcastEvent("ScrollTo:FocusedInput", ""));
|
||||
LayerView v = GeckoApp.mAppContext.getLayerView();
|
||||
if (v != null && v.isIMEEnabled()) {
|
||||
sendEventToGecko(GeckoEvent.createBroadcastEvent(
|
||||
"ScrollTo:FocusedInput", ""));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,815 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.Spanned;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Selection;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
// interface for the UI thread
|
||||
interface GeckoEditableClient {
|
||||
void sendEvent(GeckoEvent event);
|
||||
Editable getEditable();
|
||||
void setUpdateGecko(boolean update);
|
||||
void setListener(GeckoEditableListener listener);
|
||||
}
|
||||
|
||||
/* interface for the Editable to listen to the Gecko thread
|
||||
and also for the UI thread to listen to the Editable */
|
||||
interface GeckoEditableListener {
|
||||
void notifyIME(int type, int state);
|
||||
void notifyIMEEnabled(int state, String typeHint,
|
||||
String modeHint, String actionHint);
|
||||
void onSelectionChange(int start, int end);
|
||||
void onTextChange(String text, int start, int oldEnd, int newEnd);
|
||||
}
|
||||
|
||||
/*
|
||||
GeckoEditable implements only some functions of Editable
|
||||
The field mText contains the actual underlying
|
||||
SpannableStringBuilder/Editable that contains our text.
|
||||
*/
|
||||
final class GeckoEditable
|
||||
implements InvocationHandler, Editable,
|
||||
GeckoEditableClient, GeckoEditableListener {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String LOGTAG = "GeckoEditable";
|
||||
private static final int NOTIFY_IME_REPLY_EVENT = 1;
|
||||
|
||||
// Filters to implement Editable's filtering functionality
|
||||
private InputFilter[] mFilters;
|
||||
|
||||
private final SpannableStringBuilder mText;
|
||||
private final Editable mProxy;
|
||||
private GeckoEditableListener mListener;
|
||||
private final ActionQueue mActionQueue;
|
||||
|
||||
private int mSavedSelectionStart;
|
||||
private volatile int mGeckoUpdateSeqno;
|
||||
private int mUIUpdateSeqno;
|
||||
private int mLastUIUpdateSeqno;
|
||||
private boolean mUpdateGecko;
|
||||
|
||||
/* An action that alters the Editable
|
||||
|
||||
Each action corresponds to a Gecko event. While the Gecko event is being sent to the Gecko
|
||||
thread, the action stays on top of mActions queue. After the Gecko event is processed and
|
||||
replied, the action is removed from the queue
|
||||
*/
|
||||
private static final class Action {
|
||||
// For input events (keypress, etc.); use with IME_SYNCHRONIZE
|
||||
static final int TYPE_EVENT = 0;
|
||||
// For Editable.replace() call; use with IME_REPLACE_TEXT
|
||||
static final int TYPE_REPLACE_TEXT = 1;
|
||||
/* For Editable.setSpan(Selection...) call; use with IME_SYNCHRONIZE
|
||||
Note that we don't use this with IME_SET_SELECTION because we don't want to update the
|
||||
Gecko selection at the point of this action. The Gecko selection is updated only after
|
||||
UI has updated its selection (during IME_SYNCHRONIZE reply) */
|
||||
static final int TYPE_SET_SELECTION = 2;
|
||||
// For Editable.setSpan() call; use with IME_SYNCHRONIZE
|
||||
static final int TYPE_SET_SPAN = 3;
|
||||
// For Editable.removeSpan() call; use with IME_SYNCHRONIZE
|
||||
static final int TYPE_REMOVE_SPAN = 4;
|
||||
|
||||
final int mType;
|
||||
int mStart;
|
||||
int mEnd;
|
||||
CharSequence mSequence;
|
||||
Object mSpanObject;
|
||||
int mSpanFlags;
|
||||
boolean mShouldUpdate;
|
||||
|
||||
Action(int type) {
|
||||
mType = type;
|
||||
}
|
||||
|
||||
static Action newReplaceText(CharSequence text, int start, int end) {
|
||||
if (start < 0 || start > end) {
|
||||
throw new IllegalArgumentException("invalid replace text offsets");
|
||||
}
|
||||
final Action action = new Action(TYPE_REPLACE_TEXT);
|
||||
action.mSequence = text;
|
||||
action.mStart = start;
|
||||
action.mEnd = end;
|
||||
return action;
|
||||
}
|
||||
|
||||
static Action newSetSelection(int start, int end) {
|
||||
if (start < 0 || start > end) {
|
||||
throw new IllegalArgumentException("invalid selection offsets");
|
||||
}
|
||||
final Action action = new Action(TYPE_SET_SELECTION);
|
||||
action.mStart = start;
|
||||
action.mEnd = end;
|
||||
return action;
|
||||
}
|
||||
|
||||
static Action newSetSpan(Object object, int start, int end, int flags) {
|
||||
if (start < 0 || start > end) {
|
||||
throw new IllegalArgumentException("invalid span offsets");
|
||||
}
|
||||
final Action action = new Action(TYPE_SET_SPAN);
|
||||
action.mSpanObject = object;
|
||||
action.mStart = start;
|
||||
action.mEnd = end;
|
||||
action.mSpanFlags = flags;
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
/* Queue of editing actions sent to Gecko thread that
|
||||
the Gecko thread has not responded to yet */
|
||||
private final class ActionQueue {
|
||||
private final ConcurrentLinkedQueue<Action> mActions;
|
||||
private final Semaphore mActionsActive;
|
||||
|
||||
ActionQueue() {
|
||||
mActions = new ConcurrentLinkedQueue<Action>();
|
||||
mActionsActive = new Semaphore(1);
|
||||
}
|
||||
|
||||
void offer(Action action) {
|
||||
if (DEBUG) {
|
||||
GeckoApp.assertOnUiThread();
|
||||
}
|
||||
/* Events don't need update because they generate text/selection
|
||||
notifications which will do the updating for us */
|
||||
if (action.mType != Action.TYPE_EVENT) {
|
||||
action.mShouldUpdate = mUpdateGecko;
|
||||
}
|
||||
if (mActions.isEmpty()) {
|
||||
mActionsActive.acquireUninterruptibly();
|
||||
mActions.offer(action);
|
||||
} else synchronized(this) {
|
||||
// tryAcquire here in case Gecko thread has just released it
|
||||
mActionsActive.tryAcquire();
|
||||
mActions.offer(action);
|
||||
}
|
||||
switch (action.mType) {
|
||||
case Action.TYPE_EVENT:
|
||||
case Action.TYPE_SET_SELECTION:
|
||||
case Action.TYPE_SET_SPAN:
|
||||
case Action.TYPE_REMOVE_SPAN:
|
||||
GeckoAppShell.sendEventToGecko(
|
||||
GeckoEvent.createIMEEvent(GeckoEvent.IME_SYNCHRONIZE));
|
||||
break;
|
||||
case Action.TYPE_REPLACE_TEXT:
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEReplaceEvent(
|
||||
action.mStart, action.mEnd, action.mSequence.toString()));
|
||||
break;
|
||||
}
|
||||
++mUIUpdateSeqno;
|
||||
}
|
||||
|
||||
void poll() {
|
||||
if (DEBUG) {
|
||||
GeckoApp.assertOnGeckoThread();
|
||||
}
|
||||
if (mActions.isEmpty()) {
|
||||
throw new IllegalStateException("empty actions queue");
|
||||
}
|
||||
mActions.poll();
|
||||
// Don't bother locking if queue is not empty yet
|
||||
if (mActions.isEmpty()) {
|
||||
synchronized(this) {
|
||||
if (mActions.isEmpty()) {
|
||||
mActionsActive.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Action peek() {
|
||||
if (DEBUG) {
|
||||
GeckoApp.assertOnGeckoThread();
|
||||
}
|
||||
if (mActions.isEmpty()) {
|
||||
throw new IllegalStateException("empty actions queue");
|
||||
}
|
||||
return mActions.peek();
|
||||
}
|
||||
|
||||
void syncWithGecko() {
|
||||
if (DEBUG) {
|
||||
GeckoApp.assertOnUiThread();
|
||||
}
|
||||
if (!mActions.isEmpty()) {
|
||||
mActionsActive.acquireUninterruptibly();
|
||||
mActionsActive.release();
|
||||
}
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return mActions.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
GeckoEditable() {
|
||||
mActionQueue = new ActionQueue();
|
||||
mSavedSelectionStart = -1;
|
||||
mUpdateGecko = true;
|
||||
|
||||
mText = new SpannableStringBuilder();
|
||||
|
||||
final Class[] PROXY_INTERFACES = { Editable.class };
|
||||
mProxy = (Editable)Proxy.newProxyInstance(
|
||||
Editable.class.getClassLoader(),
|
||||
PROXY_INTERFACES, this);
|
||||
}
|
||||
|
||||
private static void geckoPostToUI(Runnable runnable) {
|
||||
GeckoApp.mAppContext.mMainHandler.post(runnable);
|
||||
}
|
||||
|
||||
private void geckoUpdateGecko(final boolean force) {
|
||||
/* We do not increment the seqno here, but only check it, because geckoUpdateGecko is a
|
||||
request for update. If we incremented the seqno here, geckoUpdateGecko would have
|
||||
prevented other updates from occurring */
|
||||
final int seqnoWhenPosted = mGeckoUpdateSeqno;
|
||||
|
||||
geckoPostToUI(new Runnable() {
|
||||
public void run() {
|
||||
mActionQueue.syncWithGecko();
|
||||
if (seqnoWhenPosted == mGeckoUpdateSeqno) {
|
||||
uiUpdateGecko(force);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void uiUpdateGecko(boolean force) {
|
||||
|
||||
if (!force && mUIUpdateSeqno == mLastUIUpdateSeqno) {
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "uiUpdateGecko() skipped");
|
||||
}
|
||||
return;
|
||||
}
|
||||
mLastUIUpdateSeqno = mUIUpdateSeqno;
|
||||
mActionQueue.syncWithGecko();
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "uiUpdateGecko()");
|
||||
}
|
||||
|
||||
final int selStart = mText.getSpanStart(Selection.SELECTION_START);
|
||||
final int selEnd = mText.getSpanEnd(Selection.SELECTION_END);
|
||||
int composingStart = mText.length();
|
||||
int composingEnd = 0;
|
||||
Object[] spans = mText.getSpans(0, composingStart, Object.class);
|
||||
|
||||
for (Object span : spans) {
|
||||
if ((mText.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
|
||||
composingStart = Math.min(composingStart, mText.getSpanStart(span));
|
||||
composingEnd = Math.max(composingEnd, mText.getSpanEnd(span));
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, " range = " + composingStart + "-" + composingEnd);
|
||||
Log.d(LOGTAG, " selection = " + selStart + "-" + selEnd);
|
||||
}
|
||||
if (composingStart >= composingEnd) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEEvent(
|
||||
GeckoEvent.IME_REMOVE_COMPOSITION));
|
||||
if (selStart >= 0 && selEnd >= 0) {
|
||||
GeckoAppShell.sendEventToGecko(
|
||||
GeckoEvent.createIMESelectEvent(selStart, selEnd));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (selEnd >= composingStart && selEnd <= composingEnd) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createIMERangeEvent(
|
||||
selEnd - composingStart, selEnd - composingStart,
|
||||
GeckoEvent.IME_RANGE_CARETPOSITION, 0, 0, 0));
|
||||
}
|
||||
int rangeStart = composingStart;
|
||||
do {
|
||||
int rangeType, rangeStyles = 0;
|
||||
int rangeForeColor = 0, rangeBackColor = 0;
|
||||
int rangeEnd = mText.nextSpanTransition(rangeStart, composingEnd, Object.class);
|
||||
|
||||
if (selStart > rangeStart && selStart < rangeEnd) {
|
||||
rangeEnd = selStart;
|
||||
} else if (selEnd > rangeStart && selEnd < rangeEnd) {
|
||||
rangeEnd = selEnd;
|
||||
}
|
||||
spans = mText.getSpans(rangeStart, rangeEnd, Object.class);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, " found " + spans.length + " spans @ " +
|
||||
rangeStart + "-" + rangeEnd);
|
||||
}
|
||||
|
||||
if (spans.length == 0) {
|
||||
rangeType = (selStart == rangeStart && selEnd == rangeEnd)
|
||||
? GeckoEvent.IME_RANGE_SELECTEDRAWTEXT
|
||||
: GeckoEvent.IME_RANGE_RAWINPUT;
|
||||
} else {
|
||||
rangeType = (selStart == rangeStart && selEnd == rangeEnd)
|
||||
? GeckoEvent.IME_RANGE_SELECTEDCONVERTEDTEXT
|
||||
: GeckoEvent.IME_RANGE_CONVERTEDTEXT;
|
||||
for (Object span : spans) {
|
||||
if (span instanceof UnderlineSpan) {
|
||||
rangeStyles |= GeckoEvent.IME_RANGE_UNDERLINE;
|
||||
} else if (span instanceof ForegroundColorSpan) {
|
||||
rangeStyles |= GeckoEvent.IME_RANGE_FORECOLOR;
|
||||
rangeForeColor =
|
||||
((ForegroundColorSpan)span).getForegroundColor();
|
||||
} else if (span instanceof BackgroundColorSpan) {
|
||||
rangeStyles |= GeckoEvent.IME_RANGE_BACKCOLOR;
|
||||
rangeBackColor =
|
||||
((BackgroundColorSpan)span).getBackgroundColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createIMERangeEvent(
|
||||
rangeStart - composingStart, rangeEnd - composingStart,
|
||||
rangeType, rangeStyles, rangeForeColor, rangeBackColor));
|
||||
rangeStart = rangeEnd;
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, " added " + rangeType + " : " + rangeStyles +
|
||||
" : " + Integer.toHexString(rangeForeColor) +
|
||||
" : " + Integer.toHexString(rangeBackColor));
|
||||
}
|
||||
} while (rangeStart < composingEnd);
|
||||
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createIMECompositionEvent(
|
||||
composingStart, composingEnd));
|
||||
}
|
||||
|
||||
// GeckoEditableClient interface
|
||||
|
||||
@Override
|
||||
public void sendEvent(GeckoEvent event) {
|
||||
if (DEBUG) {
|
||||
// GeckoEditableClient methods should all be called from the UI thread
|
||||
GeckoApp.assertOnUiThread();
|
||||
}
|
||||
/*
|
||||
We are actually sending two events to Gecko here,
|
||||
1. Event from the event parameter (key event, etc.)
|
||||
2. Sync event from the mActionQueue.offer call
|
||||
The first event is a normal GeckoEvent that does not reply back to us,
|
||||
the second sync event will have a reply, during which we see that there is a pending
|
||||
event-type action, and update the selection/composition/etc. accordingly.
|
||||
*/
|
||||
GeckoAppShell.sendEventToGecko(event);
|
||||
mActionQueue.offer(new Action(Action.TYPE_EVENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable getEditable() {
|
||||
if (DEBUG) {
|
||||
// GeckoEditableClient methods should all be called from the UI thread
|
||||
GeckoApp.assertOnUiThread();
|
||||
}
|
||||
return mProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpdateGecko(boolean update) {
|
||||
if (DEBUG) {
|
||||
// GeckoEditableClient methods should all be called from the UI thread
|
||||
GeckoApp.assertOnUiThread();
|
||||
}
|
||||
if (update) {
|
||||
uiUpdateGecko(false);
|
||||
}
|
||||
mUpdateGecko = update;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListener(GeckoEditableListener listener) {
|
||||
if (DEBUG) {
|
||||
// GeckoEditableClient methods should all be called from the UI thread
|
||||
GeckoApp.assertOnUiThread();
|
||||
}
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
// GeckoEditableListener interface
|
||||
|
||||
void geckoActionReply() {
|
||||
if (DEBUG) {
|
||||
// GeckoEditableListener methods should all be called from the Gecko thread
|
||||
GeckoApp.assertOnGeckoThread();
|
||||
}
|
||||
final Action action = mActionQueue.peek();
|
||||
|
||||
switch (action.mType) {
|
||||
case Action.TYPE_SET_SELECTION:
|
||||
final int len = mText.length();
|
||||
final int selStart = Math.min(action.mStart, len);
|
||||
final int selEnd = Math.min(action.mEnd, len);
|
||||
|
||||
if (selStart < action.mStart || selEnd < action.mEnd) {
|
||||
Log.w(LOGTAG, "IME sync error: selection out of bounds");
|
||||
}
|
||||
Selection.setSelection(mText, selStart, selEnd);
|
||||
geckoPostToUI(new Runnable() {
|
||||
public void run() {
|
||||
mActionQueue.syncWithGecko();
|
||||
final int start = Selection.getSelectionStart(mText);
|
||||
final int end = Selection.getSelectionEnd(mText);
|
||||
if (mListener != null &&
|
||||
selStart == start && selEnd == end) {
|
||||
// There has not been another new selection in the mean time that
|
||||
// made this notification out-of-date
|
||||
mListener.onSelectionChange(start, end);
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case Action.TYPE_SET_SPAN:
|
||||
mText.setSpan(action.mSpanObject, action.mStart, action.mEnd, action.mSpanFlags);
|
||||
break;
|
||||
}
|
||||
if (action.mShouldUpdate) {
|
||||
geckoUpdateGecko(false);
|
||||
}
|
||||
mActionQueue.poll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyIME(final int type, final int state) {
|
||||
if (DEBUG) {
|
||||
// GeckoEditableListener methods should all be called from the Gecko thread
|
||||
GeckoApp.assertOnGeckoThread();
|
||||
}
|
||||
if (type == NOTIFY_IME_REPLY_EVENT) {
|
||||
geckoActionReply();
|
||||
return;
|
||||
}
|
||||
geckoPostToUI(new Runnable() {
|
||||
public void run() {
|
||||
// Make sure there are no other things going on
|
||||
mActionQueue.syncWithGecko();
|
||||
if (mListener != null) {
|
||||
mListener.notifyIME(type, state);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyIMEEnabled(final int state, final String typeHint,
|
||||
final String modeHint, final String actionHint) {
|
||||
if (DEBUG) {
|
||||
// GeckoEditableListener methods should all be called from the Gecko thread
|
||||
GeckoApp.assertOnGeckoThread();
|
||||
}
|
||||
geckoPostToUI(new Runnable() {
|
||||
public void run() {
|
||||
// Make sure there are no other things going on
|
||||
mActionQueue.syncWithGecko();
|
||||
if (mListener != null) {
|
||||
mListener.notifyIMEEnabled(state, typeHint,
|
||||
modeHint, actionHint);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionChange(final int start, final int end) {
|
||||
if (DEBUG) {
|
||||
// GeckoEditableListener methods should all be called from the Gecko thread
|
||||
GeckoApp.assertOnGeckoThread();
|
||||
}
|
||||
if (start < 0 || start > end || end > mText.length()) {
|
||||
throw new IllegalArgumentException("invalid selection notification range");
|
||||
}
|
||||
final int seqnoWhenPosted = ++mGeckoUpdateSeqno;
|
||||
|
||||
geckoPostToUI(new Runnable() {
|
||||
public void run() {
|
||||
mActionQueue.syncWithGecko();
|
||||
/* check to see there has not been another action that potentially changed the
|
||||
selection. If so, we can skip this update because we know there is another
|
||||
update right after this one that will replace the effect of this update */
|
||||
if (mGeckoUpdateSeqno == seqnoWhenPosted) {
|
||||
/* In this case, Gecko's selection has changed and it's notifying us to change
|
||||
Java's selection. In the normal case, whenever Java's selection changes,
|
||||
we go back and set Gecko's selection as well. However, in this case,
|
||||
since Gecko's selection is already up-to-date, we skip this step. */
|
||||
boolean oldUpdateGecko = mUpdateGecko;
|
||||
mUpdateGecko = false;
|
||||
Selection.setSelection(mProxy, start, end);
|
||||
mUpdateGecko = oldUpdateGecko;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChange(final String text, final int start,
|
||||
final int unboundedOldEnd, final int unboundedNewEnd) {
|
||||
if (DEBUG) {
|
||||
// GeckoEditableListener methods should all be called from the Gecko thread
|
||||
GeckoApp.assertOnGeckoThread();
|
||||
}
|
||||
if (start < 0 || start > unboundedOldEnd) {
|
||||
throw new IllegalArgumentException("invalid text notification range");
|
||||
}
|
||||
/* For the "end" parameters, Gecko can pass in a large
|
||||
number to denote "end of the text". Fix that here */
|
||||
final int oldEnd = unboundedOldEnd > mText.length() ? mText.length() : unboundedOldEnd;
|
||||
// new end should always match text
|
||||
if (unboundedNewEnd < (start + text.length())) {
|
||||
throw new IllegalArgumentException("newEnd does not match text");
|
||||
}
|
||||
final int newEnd = start + text.length();
|
||||
|
||||
if (!mActionQueue.isEmpty()) {
|
||||
final Action action = mActionQueue.peek();
|
||||
if (action.mType == Action.TYPE_REPLACE_TEXT &&
|
||||
action.mStart == start &&
|
||||
text.equals(action.mSequence.toString())) {
|
||||
// Replace using saved text to preserve spans
|
||||
mText.replace(start, oldEnd, action.mSequence,
|
||||
0, action.mSequence.length());
|
||||
} else {
|
||||
mText.replace(start, oldEnd, text, 0, text.length());
|
||||
}
|
||||
} else {
|
||||
mText.replace(start, oldEnd, text, 0, text.length());
|
||||
geckoPostToUI(new Runnable() {
|
||||
public void run() {
|
||||
if (mListener != null) {
|
||||
mListener.onTextChange(text, start, oldEnd, newEnd);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// InvocationHandler interface
|
||||
|
||||
private static StringBuilder debugAppend(StringBuilder sb, Object obj) {
|
||||
if (obj == null) {
|
||||
sb.append("null");
|
||||
} else if (obj instanceof GeckoEditable) {
|
||||
sb.append("GeckoEditable");
|
||||
} else if (Proxy.isProxyClass(obj.getClass())) {
|
||||
debugAppend(sb, Proxy.getInvocationHandler(obj));
|
||||
} else if (obj instanceof CharSequence) {
|
||||
sb.append("\"").append(obj.toString().replace('\n', '\u21b2')).append("\"");
|
||||
} else if (obj.getClass().isArray()) {
|
||||
Class cls = obj.getClass();
|
||||
sb.append(cls.getComponentType().getSimpleName()).append("[")
|
||||
.append(java.lang.reflect.Array.getLength(obj)).append("]");
|
||||
} else {
|
||||
sb.append(obj.toString());
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Throwable {
|
||||
Object target;
|
||||
final Class methodInterface = method.getDeclaringClass();
|
||||
if (DEBUG) {
|
||||
// Editable methods should all be called from the UI thread
|
||||
GeckoApp.assertOnUiThread();
|
||||
}
|
||||
if (methodInterface == Editable.class ||
|
||||
methodInterface == Appendable.class ||
|
||||
methodInterface == Spannable.class) {
|
||||
// Method alters the Editable; route calls to our implementation
|
||||
target = this;
|
||||
} else {
|
||||
// Method queries the Editable; must sync with Gecko first
|
||||
// then call on the inner Editable itself
|
||||
mActionQueue.syncWithGecko();
|
||||
target = mText;
|
||||
}
|
||||
Object ret = method.invoke(target, args);
|
||||
if (DEBUG) {
|
||||
StringBuilder log = new StringBuilder(method.getName());
|
||||
log.append("(");
|
||||
for (Object arg : args) {
|
||||
debugAppend(log, arg).append(", ");
|
||||
}
|
||||
if (args.length > 0) {
|
||||
log.setLength(log.length() - 2);
|
||||
}
|
||||
if (method.getReturnType().equals(Void.TYPE)) {
|
||||
log.append(")");
|
||||
} else {
|
||||
debugAppend(log.append(") = "), ret);
|
||||
}
|
||||
Log.d(LOGTAG, log.toString());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Spannable interface
|
||||
|
||||
private static boolean isCompositionSpan(Object what, int flags) {
|
||||
return (flags & Spanned.SPAN_COMPOSING) != 0 ||
|
||||
what instanceof UnderlineSpan ||
|
||||
what instanceof ForegroundColorSpan ||
|
||||
what instanceof BackgroundColorSpan;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSpan(Object what) {
|
||||
if (what == Selection.SELECTION_START ||
|
||||
what == Selection.SELECTION_END) {
|
||||
Log.w(LOGTAG, "selection removed with removeSpan()");
|
||||
}
|
||||
// Okay to remove immediately
|
||||
mText.removeSpan(what);
|
||||
if (mUpdateGecko) {
|
||||
mActionQueue.offer(new Action(Action.TYPE_REMOVE_SPAN));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpan(Object what, int start, int end, int flags) {
|
||||
if (what == Selection.SELECTION_START) {
|
||||
if ((flags & Spanned.SPAN_INTERMEDIATE) != 0) {
|
||||
// We will get the end offset next, just save the start for now
|
||||
mSavedSelectionStart = start;
|
||||
} else {
|
||||
mActionQueue.offer(Action.newSetSelection(start, -1));
|
||||
}
|
||||
} else if (what == Selection.SELECTION_END) {
|
||||
mActionQueue.offer(Action.newSetSelection(mSavedSelectionStart, end));
|
||||
mSavedSelectionStart = -1;
|
||||
} else {
|
||||
mActionQueue.offer(Action.newSetSpan(what, start, end, flags));
|
||||
}
|
||||
}
|
||||
|
||||
// Appendable interface
|
||||
|
||||
@Override
|
||||
public Editable append(CharSequence text) {
|
||||
return replace(length(), length(), text, 0, text.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable append(CharSequence text, int start, int end) {
|
||||
return replace(length(), length(), text, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable append(char text) {
|
||||
return replace(length(), length(), String.valueOf(text), 0, 1);
|
||||
}
|
||||
|
||||
// Editable interface
|
||||
|
||||
@Override
|
||||
public InputFilter[] getFilters() {
|
||||
return mFilters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilters(InputFilter[] filters) {
|
||||
mFilters = filters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSpans() {
|
||||
/* XXX this clears the selection spans too,
|
||||
but there is no way to clear the corresponding selection in Gecko */
|
||||
Log.w(LOGTAG, "selection cleared with clearSpans()");
|
||||
mText.clearSpans();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable replace(int st, int en,
|
||||
CharSequence source, int start, int end) {
|
||||
|
||||
CharSequence text = source;
|
||||
if (start < 0 || start > end || end > text.length()) {
|
||||
throw new IllegalArgumentException("invalid replace offsets");
|
||||
}
|
||||
if (start != 0 || end != text.length()) {
|
||||
text = text.subSequence(start, end);
|
||||
}
|
||||
if (mFilters != null) {
|
||||
// Filter text before sending the request to Gecko
|
||||
for (int i = 0; i < mFilters.length; ++i) {
|
||||
final CharSequence cs = mFilters[i].filter(
|
||||
text, 0, text.length(), mProxy, st, en);
|
||||
if (cs != null) {
|
||||
text = cs;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (text == source) {
|
||||
// Always create a copy
|
||||
text = new SpannableString(source);
|
||||
}
|
||||
mActionQueue.offer(Action.newReplaceText(text,
|
||||
Math.min(st, en), Math.max(st, en)));
|
||||
return mProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
replace(0, length(), "", 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable delete(int st, int en) {
|
||||
return replace(st, en, "", 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable insert(int where, CharSequence text,
|
||||
int start, int end) {
|
||||
return replace(where, where, text, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable insert(int where, CharSequence text) {
|
||||
return replace(where, where, text, 0, text.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable replace(int st, int en, CharSequence text) {
|
||||
return replace(st, en, text, 0, text.length());
|
||||
}
|
||||
|
||||
/* GetChars interface */
|
||||
|
||||
@Override
|
||||
public void getChars(int start, int end, char[] dest, int destoff) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/* Spanned interface */
|
||||
|
||||
@Override
|
||||
public int getSpanEnd(Object tag) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpanFlags(Object tag) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpanStart(Object tag) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] getSpans(int start, int end, Class<T> type) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextSpanTransition(int start, int limit, Class type) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/* CharSequence interface */
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
|
@ -80,14 +80,12 @@ public class GeckoEvent {
|
|||
private static final int DOM_KEY_LOCATION_MOBILE = 4;
|
||||
private static final int DOM_KEY_LOCATION_JOYSTICK = 5;
|
||||
|
||||
public static final int IME_COMPOSITION_END = 0;
|
||||
public static final int IME_COMPOSITION_BEGIN = 1;
|
||||
public static final int IME_SET_TEXT = 2;
|
||||
public static final int IME_GET_TEXT = 3;
|
||||
public static final int IME_DELETE_TEXT = 4;
|
||||
public static final int IME_SET_SELECTION = 5;
|
||||
public static final int IME_GET_SELECTION = 6;
|
||||
public static final int IME_ADD_RANGE = 7;
|
||||
public static final int IME_SYNCHRONIZE = 0;
|
||||
public static final int IME_REPLACE_TEXT = 1;
|
||||
public static final int IME_SET_SELECTION = 2;
|
||||
public static final int IME_ADD_COMPOSITION_RANGE = 3;
|
||||
public static final int IME_UPDATE_COMPOSITION = 4;
|
||||
public static final int IME_REMOVE_COMPOSITION = 5;
|
||||
|
||||
public static final int IME_RANGE_CARETPOSITION = 1;
|
||||
public static final int IME_RANGE_RAWINPUT = 2;
|
||||
|
@ -118,7 +116,8 @@ public class GeckoEvent {
|
|||
public int mMetaState, mFlags;
|
||||
public int mKeyCode, mUnicodeChar;
|
||||
public int mRepeatCount;
|
||||
public int mOffset, mCount;
|
||||
public int mCount;
|
||||
public int mStart, mEnd;
|
||||
public String mCharacters, mCharactersExtra;
|
||||
public int mRangeType, mRangeStyles;
|
||||
public int mRangeForeColor, mRangeBackColor;
|
||||
|
@ -459,44 +458,51 @@ public class GeckoEvent {
|
|||
return event;
|
||||
}
|
||||
|
||||
public static GeckoEvent createIMEEvent(int imeAction, int offset, int count) {
|
||||
public static GeckoEvent createIMEEvent(int action) {
|
||||
GeckoEvent event = new GeckoEvent(IME_EVENT);
|
||||
event.mAction = imeAction;
|
||||
event.mOffset = offset;
|
||||
event.mCount = count;
|
||||
event.mAction = action;
|
||||
return event;
|
||||
}
|
||||
|
||||
private void InitIMERange(int action, int offset, int count,
|
||||
int rangeType, int rangeStyles,
|
||||
int rangeForeColor, int rangeBackColor) {
|
||||
mAction = action;
|
||||
mOffset = offset;
|
||||
mCount = count;
|
||||
mRangeType = rangeType;
|
||||
mRangeStyles = rangeStyles;
|
||||
mRangeForeColor = rangeForeColor;
|
||||
mRangeBackColor = rangeBackColor;
|
||||
return;
|
||||
}
|
||||
|
||||
public static GeckoEvent createIMERangeEvent(int offset, int count,
|
||||
int rangeType, int rangeStyles,
|
||||
int rangeForeColor, int rangeBackColor,
|
||||
String text) {
|
||||
public static GeckoEvent createIMEReplaceEvent(int start, int end,
|
||||
String text) {
|
||||
GeckoEvent event = new GeckoEvent(IME_EVENT);
|
||||
event.InitIMERange(IME_SET_TEXT, offset, count, rangeType, rangeStyles,
|
||||
rangeForeColor, rangeBackColor);
|
||||
event.mAction = IME_REPLACE_TEXT;
|
||||
event.mStart = start;
|
||||
event.mEnd = end;
|
||||
event.mCharacters = text;
|
||||
return event;
|
||||
}
|
||||
|
||||
public static GeckoEvent createIMERangeEvent(int offset, int count,
|
||||
int rangeType, int rangeStyles,
|
||||
int rangeForeColor, int rangeBackColor) {
|
||||
public static GeckoEvent createIMESelectEvent(int start, int end) {
|
||||
GeckoEvent event = new GeckoEvent(IME_EVENT);
|
||||
event.InitIMERange(IME_ADD_RANGE, offset, count, rangeType, rangeStyles,
|
||||
rangeForeColor, rangeBackColor);
|
||||
event.mAction = IME_SET_SELECTION;
|
||||
event.mStart = start;
|
||||
event.mEnd = end;
|
||||
return event;
|
||||
}
|
||||
|
||||
public static GeckoEvent createIMECompositionEvent(int start, int end) {
|
||||
GeckoEvent event = new GeckoEvent(IME_EVENT);
|
||||
event.mAction = IME_UPDATE_COMPOSITION;
|
||||
event.mStart = start;
|
||||
event.mEnd = end;
|
||||
return event;
|
||||
}
|
||||
|
||||
public static GeckoEvent createIMERangeEvent(int start,
|
||||
int end, int rangeType,
|
||||
int rangeStyles,
|
||||
int rangeForeColor,
|
||||
int rangeBackColor) {
|
||||
GeckoEvent event = new GeckoEvent(IME_EVENT);
|
||||
event.mAction = IME_ADD_COMPOSITION_RANGE;
|
||||
event.mStart = start;
|
||||
event.mEnd = end;
|
||||
event.mRangeType = rangeType;
|
||||
event.mRangeStyles = rangeStyles;
|
||||
event.mRangeForeColor = rangeForeColor;
|
||||
event.mRangeBackColor = rangeBackColor;
|
||||
return event;
|
||||
}
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -32,6 +32,7 @@ UTIL_JAVA_FILES := \
|
|||
INISection.java \
|
||||
util/EventDispatcher.java \
|
||||
util/FloatUtils.java \
|
||||
util/LruCache.java \
|
||||
$(NULL)
|
||||
|
||||
FENNEC_JAVA_FILES = \
|
||||
|
@ -76,6 +77,7 @@ FENNEC_JAVA_FILES = \
|
|||
GeckoAppShell.java \
|
||||
GeckoBatteryManager.java \
|
||||
GeckoConnectivityReceiver.java \
|
||||
GeckoEditable.java \
|
||||
GeckoEvent.java \
|
||||
GeckoHalDefines.java \
|
||||
GeckoInputConnection.java \
|
||||
|
|
|
@ -156,6 +156,8 @@ class MemoryMonitor extends BroadcastReceiver {
|
|||
}
|
||||
ScreenshotHandler.disableScreenshot(false);
|
||||
GeckoAppShell.geckoEventSync();
|
||||
|
||||
GeckoApp.mAppContext.getFavicons().clearMemCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ public final class ScreenshotHandler implements Runnable {
|
|||
private static final int BYTES_FOR_16BPP = 2;
|
||||
private static final int MAX_PIXELS_PER_SLICE = 100000;
|
||||
|
||||
private static boolean sDisableScreenshot;
|
||||
private static boolean sDisableScreenshot = true;
|
||||
private static boolean sForceDisabled;
|
||||
private static ScreenshotHandler sInstance;
|
||||
|
||||
|
|
|
@ -7,8 +7,10 @@ package org.mozilla.gecko;
|
|||
|
||||
import org.mozilla.gecko.AwesomeBar.ContextMenuSubject;
|
||||
import org.mozilla.gecko.db.BrowserContract.Combined;
|
||||
import org.mozilla.gecko.db.BrowserContract.Images;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.db.BrowserDB.URLColumns;
|
||||
import org.mozilla.gecko.util.GeckoAsyncTask;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
@ -18,8 +20,13 @@ import org.json.JSONObject;
|
|||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
@ -49,6 +56,7 @@ import android.widget.TextView;
|
|||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
||||
public static final String LOGTAG = "ALL_PAGES";
|
||||
|
@ -68,6 +76,11 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
|||
private LinearLayout mAllPagesView;
|
||||
private boolean mAnimateSuggestions;
|
||||
private View mSuggestionsOptInPrompt;
|
||||
private Handler mHandler;
|
||||
|
||||
private static final int MESSAGE_LOAD_FAVICONS = 1;
|
||||
private static final int MESSAGE_UPDATE_FAVICONS = 2;
|
||||
private static final int DELAY_SHOW_THUMBNAILS = 550;
|
||||
|
||||
private class SearchEntryViewHolder {
|
||||
public FlowLayout suggestionView;
|
||||
|
@ -122,9 +135,14 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
|||
((Activity)mContext).registerForContextMenu(mView);
|
||||
mView.setTag(TAG);
|
||||
AwesomeBarCursorAdapter adapter = getCursorAdapter();
|
||||
((ListView)mView).setAdapter(adapter);
|
||||
mView.setOnTouchListener(mListListener);
|
||||
|
||||
ListView listView = (ListView) mView;
|
||||
listView.setAdapter(adapter);
|
||||
listView.setOnTouchListener(mListListener);
|
||||
|
||||
mHandler = new AllPagesHandler();
|
||||
}
|
||||
|
||||
return (ListView)mView;
|
||||
}
|
||||
|
||||
|
@ -138,6 +156,10 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
|||
Cursor cursor = adapter.getCursor();
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
|
||||
mHandler.removeMessages(MESSAGE_UPDATE_FAVICONS);
|
||||
mHandler.removeMessages(MESSAGE_LOAD_FAVICONS);
|
||||
mHandler = null;
|
||||
}
|
||||
|
||||
public void filter(String searchTerm) {
|
||||
|
@ -196,6 +218,8 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
|||
Cursor c = BrowserDB.filter(getContentResolver(), constraint, MAX_RESULTS);
|
||||
c.getCount();
|
||||
|
||||
postLoadFavicons();
|
||||
|
||||
long end = SystemClock.uptimeMillis();
|
||||
int time = (int)(end - start);
|
||||
Log.i(LOGTAG, "Got cursor in " + time + "ms");
|
||||
|
@ -398,8 +422,8 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
|||
|
||||
updateTitle(viewHolder.titleView, cursor);
|
||||
updateUrl(viewHolder.urlView, cursor);
|
||||
updateFavicon(viewHolder.faviconView, cursor);
|
||||
updateBookmarkIcon(viewHolder.bookmarkIconView, cursor);
|
||||
displayFavicon(viewHolder);
|
||||
}
|
||||
|
||||
return convertView;
|
||||
|
@ -706,4 +730,129 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
|||
private void unregisterEventListener(String event) {
|
||||
GeckoAppShell.getEventDispatcher().unregisterEventListener(event, this);
|
||||
}
|
||||
|
||||
private List<String> getUrlsWithoutFavicon() {
|
||||
List<String> urls = new ArrayList<String>();
|
||||
|
||||
Cursor c = mCursorAdapter.getCursor();
|
||||
if (c == null || !c.moveToFirst())
|
||||
return urls;
|
||||
|
||||
do {
|
||||
final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
|
||||
|
||||
// We only want to load favicons from DB if they are not in the
|
||||
// memory cache yet.
|
||||
Favicons favicons = GeckoApp.mAppContext.getFavicons();
|
||||
if (favicons.getFaviconFromMemCache(url) != null)
|
||||
continue;
|
||||
|
||||
urls.add(url);
|
||||
} while (c.moveToNext());
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
public void storeFaviconsInMemCache(Cursor c) {
|
||||
try {
|
||||
if (c == null || !c.moveToFirst())
|
||||
return;
|
||||
|
||||
Favicons favicons = GeckoApp.mAppContext.getFavicons();
|
||||
|
||||
do {
|
||||
final String url = c.getString(c.getColumnIndexOrThrow(Images.URL));
|
||||
final byte[] b = c.getBlob(c.getColumnIndexOrThrow(Images.FAVICON));
|
||||
if (b == null)
|
||||
continue;
|
||||
|
||||
Bitmap favicon = BitmapFactory.decodeByteArray(b, 0, b.length);
|
||||
if (favicon == null)
|
||||
continue;
|
||||
|
||||
Drawable faviconDrawable = new BitmapDrawable(getResources(), favicon);
|
||||
favicons.putFaviconInMemCache(url, faviconDrawable);
|
||||
} while (c.moveToNext());
|
||||
} finally {
|
||||
if (c != null)
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFaviconsForCurrentResults() {
|
||||
final List<String> urls = getUrlsWithoutFavicon();
|
||||
if (urls.size() == 0)
|
||||
return;
|
||||
|
||||
(new GeckoAsyncTask<Void, Void, Cursor>(GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
|
||||
@Override
|
||||
public Cursor doInBackground(Void... params) {
|
||||
return BrowserDB.getFaviconsForUrls(getContentResolver(), urls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostExecute(Cursor c) {
|
||||
storeFaviconsInMemCache(c);
|
||||
postUpdateFavicons();
|
||||
}
|
||||
}).execute();
|
||||
}
|
||||
|
||||
private void displayFavicon(AwesomeEntryViewHolder viewHolder) {
|
||||
final String url = viewHolder.urlView.getText().toString();
|
||||
Favicons favicons = GeckoApp.mAppContext.getFavicons();
|
||||
viewHolder.faviconView.setImageDrawable(favicons.getFaviconFromMemCache(url));
|
||||
}
|
||||
|
||||
private void updateFavicons() {
|
||||
ListView listView = (ListView) mView;
|
||||
for (int i = 0; i < listView.getChildCount(); i++) {
|
||||
final View view = listView.getChildAt(i);
|
||||
final Object tag = view.getTag();
|
||||
|
||||
if (tag == null || !(tag instanceof AwesomeEntryViewHolder))
|
||||
continue;
|
||||
|
||||
final AwesomeEntryViewHolder viewHolder = (AwesomeEntryViewHolder) tag;
|
||||
displayFavicon(viewHolder);
|
||||
}
|
||||
|
||||
mView.invalidate();
|
||||
}
|
||||
|
||||
private void postUpdateFavicons() {
|
||||
if (mHandler == null)
|
||||
return;
|
||||
|
||||
Message msg = mHandler.obtainMessage(MESSAGE_UPDATE_FAVICONS,
|
||||
AllPagesTab.this);
|
||||
|
||||
mHandler.removeMessages(MESSAGE_UPDATE_FAVICONS);
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
private void postLoadFavicons() {
|
||||
if (mHandler == null)
|
||||
return;
|
||||
|
||||
Message msg = mHandler.obtainMessage(MESSAGE_LOAD_FAVICONS,
|
||||
AllPagesTab.this);
|
||||
|
||||
mHandler.removeMessages(MESSAGE_LOAD_FAVICONS);
|
||||
mHandler.sendMessageDelayed(msg, 200);
|
||||
}
|
||||
|
||||
private class AllPagesHandler extends Handler {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MESSAGE_LOAD_FAVICONS:
|
||||
loadFaviconsForCurrentResults();
|
||||
break;
|
||||
case MESSAGE_UPDATE_FAVICONS:
|
||||
updateFavicons();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ import android.database.ContentObserver;
|
|||
import android.database.Cursor;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BrowserDB {
|
||||
public static String ABOUT_PAGES_URL_FILTER = "about:%";
|
||||
|
||||
|
@ -73,12 +75,16 @@ public class BrowserDB {
|
|||
|
||||
public BitmapDrawable getFaviconForUrl(ContentResolver cr, String uri);
|
||||
|
||||
public Cursor getFaviconsForUrls(ContentResolver cr, List<String> urls);
|
||||
|
||||
public void updateFaviconForUrl(ContentResolver cr, String uri, BitmapDrawable favicon);
|
||||
|
||||
public void updateThumbnailForUrl(ContentResolver cr, String uri, BitmapDrawable thumbnail);
|
||||
|
||||
public byte[] getThumbnailForUrl(ContentResolver cr, String uri);
|
||||
|
||||
public Cursor getThumbnailsForUrls(ContentResolver cr, List<String> urls);
|
||||
|
||||
public void removeThumbnails(ContentResolver cr);
|
||||
|
||||
public void registerBookmarkObserver(ContentResolver cr, ContentObserver observer);
|
||||
|
@ -186,6 +192,10 @@ public class BrowserDB {
|
|||
return sDb.getFaviconForUrl(cr, uri);
|
||||
}
|
||||
|
||||
public static Cursor getFaviconsForUrls(ContentResolver cr, List<String> urls) {
|
||||
return sDb.getFaviconsForUrls(cr, urls);
|
||||
}
|
||||
|
||||
public static void updateFaviconForUrl(ContentResolver cr, String uri, BitmapDrawable favicon) {
|
||||
sDb.updateFaviconForUrl(cr, uri, favicon);
|
||||
}
|
||||
|
@ -198,6 +208,10 @@ public class BrowserDB {
|
|||
return sDb.getThumbnailForUrl(cr, uri);
|
||||
}
|
||||
|
||||
public static Cursor getThumbnailsForUrls(ContentResolver cr, List<String> urls) {
|
||||
return sDb.getThumbnailsForUrls(cr, urls);
|
||||
}
|
||||
|
||||
public static void removeThumbnails(ContentResolver cr) {
|
||||
sDb.removeThumbnails(cr);
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ public class BrowserProvider extends ContentProvider {
|
|||
|
||||
static final String DATABASE_NAME = "browser.db";
|
||||
|
||||
static final int DATABASE_VERSION = 11;
|
||||
static final int DATABASE_VERSION = 12;
|
||||
|
||||
// Maximum age of deleted records to be cleaned up (20 days in ms)
|
||||
static final long MAX_AGE_OF_DELETED_RECORDS = 86400000 * 20;
|
||||
|
@ -100,6 +100,8 @@ public class BrowserProvider extends ContentProvider {
|
|||
|
||||
static final String VIEW_BOOKMARKS_WITH_IMAGES = "bookmarks_with_images";
|
||||
static final String VIEW_HISTORY_WITH_IMAGES = "history_with_images";
|
||||
|
||||
static final String VIEW_COMBINED = "combined";
|
||||
static final String VIEW_COMBINED_WITH_IMAGES = "combined_with_images";
|
||||
|
||||
// Bookmark matches
|
||||
|
@ -644,6 +646,74 @@ public class BrowserProvider extends ContentProvider {
|
|||
" ON " + Combined.URL + " = " + qualifyColumn(TABLE_IMAGES, Images.URL));
|
||||
}
|
||||
|
||||
private void createCombinedViewOn12(SQLiteDatabase db) {
|
||||
debug("Creating " + VIEW_COMBINED + " view");
|
||||
|
||||
db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED + " AS" +
|
||||
" SELECT " + Combined.BOOKMARK_ID + ", " +
|
||||
Combined.HISTORY_ID + ", " +
|
||||
// We need to return an _id column because CursorAdapter requires it for its
|
||||
// default implementation for the getItemId() method. However, since
|
||||
// we're not using this feature in the parts of the UI using this view,
|
||||
// we can just use 0 for all rows.
|
||||
"0 AS " + Combined._ID + ", " +
|
||||
Combined.URL + ", " +
|
||||
Combined.TITLE + ", " +
|
||||
Combined.VISITS + ", " +
|
||||
Combined.DISPLAY + ", " +
|
||||
Combined.DATE_LAST_VISITED +
|
||||
" FROM (" +
|
||||
// Bookmarks without history.
|
||||
" SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " +
|
||||
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " +
|
||||
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " +
|
||||
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " +
|
||||
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " +
|
||||
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
|
||||
"-1 AS " + Combined.HISTORY_ID + ", " +
|
||||
"-1 AS " + Combined.VISITS + ", " +
|
||||
"-1 AS " + Combined.DATE_LAST_VISITED +
|
||||
" FROM " + TABLE_BOOKMARKS +
|
||||
" WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " +
|
||||
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " +
|
||||
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) +
|
||||
" NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" +
|
||||
" UNION ALL" +
|
||||
// History with and without bookmark.
|
||||
" SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " +
|
||||
qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " +
|
||||
qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " +
|
||||
// Prioritze bookmark titles over history titles, since the user may have
|
||||
// customized the title for a bookmark.
|
||||
"COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " +
|
||||
qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " +
|
||||
// Only use DISPLAY_READER if the matching bookmark entry inside reading
|
||||
// list folder is not marked as deleted.
|
||||
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " +
|
||||
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID +
|
||||
" THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " +
|
||||
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
|
||||
qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " +
|
||||
qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " +
|
||||
qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED +
|
||||
" FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS +
|
||||
" ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) +
|
||||
" WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " +
|
||||
qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" +
|
||||
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " +
|
||||
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " +
|
||||
")");
|
||||
|
||||
debug("Creating " + VIEW_COMBINED_WITH_IMAGES + " view");
|
||||
|
||||
db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED_WITH_IMAGES + " AS" +
|
||||
" SELECT *, " +
|
||||
qualifyColumn(TABLE_IMAGES, Images.FAVICON) + " AS " + Combined.FAVICON + ", " +
|
||||
qualifyColumn(TABLE_IMAGES, Images.THUMBNAIL) + " AS " + Combined.THUMBNAIL +
|
||||
" FROM " + VIEW_COMBINED + " LEFT OUTER JOIN " + TABLE_IMAGES +
|
||||
" ON " + Combined.URL + " = " + qualifyColumn(TABLE_IMAGES, Images.URL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
debug("Creating browser.db: " + db.getPath());
|
||||
|
@ -651,10 +721,10 @@ public class BrowserProvider extends ContentProvider {
|
|||
createBookmarksTable(db);
|
||||
createHistoryTable(db);
|
||||
createImagesTable(db);
|
||||
createCombinedViewOn12(db);
|
||||
|
||||
createBookmarksWithImagesView(db);
|
||||
createHistoryWithImagesView(db);
|
||||
createCombinedWithImagesViewOn11(db);
|
||||
|
||||
createOrUpdateSpecialFolder(db, Bookmarks.PLACES_FOLDER_GUID,
|
||||
R.string.bookmarks_folder_places, 0);
|
||||
|
@ -1091,6 +1161,13 @@ public class BrowserProvider extends ContentProvider {
|
|||
createCombinedWithImagesViewOn11(db);
|
||||
}
|
||||
|
||||
private void upgradeDatabaseFrom11to12(SQLiteDatabase db) {
|
||||
debug("Dropping view: " + VIEW_COMBINED_WITH_IMAGES);
|
||||
db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED_WITH_IMAGES);
|
||||
|
||||
createCombinedViewOn12(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
debug("Upgrading browser.db: " + db.getPath() + " from " +
|
||||
|
@ -1141,6 +1218,10 @@ public class BrowserProvider extends ContentProvider {
|
|||
case 11:
|
||||
upgradeDatabaseFrom10to11(db);
|
||||
break;
|
||||
|
||||
case 12:
|
||||
upgradeDatabaseFrom11to12(db);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1931,7 +2012,11 @@ public class BrowserProvider extends ContentProvider {
|
|||
groupBy = Combined.URL;
|
||||
|
||||
qb.setProjectionMap(COMBINED_PROJECTION_MAP);
|
||||
qb.setTables(VIEW_COMBINED_WITH_IMAGES);
|
||||
|
||||
if (hasImagesInProjection(projection))
|
||||
qb.setTables(VIEW_COMBINED_WITH_IMAGES);
|
||||
else
|
||||
qb.setTables(VIEW_COMBINED);
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import android.util.Log;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
// Calculate these once, at initialization. isLoggable is too expensive to
|
||||
|
@ -181,7 +182,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
|||
new String[] { Combined._ID,
|
||||
Combined.URL,
|
||||
Combined.TITLE,
|
||||
Combined.FAVICON,
|
||||
Combined.DISPLAY,
|
||||
Combined.BOOKMARK_ID,
|
||||
Combined.HISTORY_ID },
|
||||
|
@ -194,8 +194,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
|||
return filterAllSites(cr,
|
||||
new String[] { Combined._ID,
|
||||
Combined.URL,
|
||||
Combined.TITLE,
|
||||
Combined.THUMBNAIL },
|
||||
Combined.TITLE },
|
||||
"",
|
||||
limit,
|
||||
BrowserDB.ABOUT_PAGES_URL_FILTER);
|
||||
|
@ -619,6 +618,27 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
|||
return new BitmapDrawable(bitmap);
|
||||
}
|
||||
|
||||
public Cursor getFaviconsForUrls(ContentResolver cr, List<String> urls) {
|
||||
StringBuffer selection = new StringBuffer();
|
||||
String[] selectionArgs = new String[urls.size()];
|
||||
|
||||
for (int i = 0; i < urls.size(); i++) {
|
||||
final String url = urls.get(i);
|
||||
|
||||
if (i > 0)
|
||||
selection.append(" OR ");
|
||||
|
||||
selection.append(Images.URL + " = ?");
|
||||
selectionArgs[i] = url;
|
||||
}
|
||||
|
||||
return cr.query(mImagesUriWithProfile,
|
||||
new String[] { Images.URL, Images.FAVICON },
|
||||
selection.toString(),
|
||||
selectionArgs,
|
||||
null);
|
||||
}
|
||||
|
||||
public void updateFaviconForUrl(ContentResolver cr, String uri,
|
||||
BitmapDrawable favicon) {
|
||||
Bitmap bitmap = favicon.getBitmap();
|
||||
|
@ -687,6 +707,27 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
|||
return b;
|
||||
}
|
||||
|
||||
public Cursor getThumbnailsForUrls(ContentResolver cr, List<String> urls) {
|
||||
StringBuffer selection = new StringBuffer();
|
||||
String[] selectionArgs = new String[urls.size()];
|
||||
|
||||
for (int i = 0; i < urls.size(); i++) {
|
||||
final String url = urls.get(i);
|
||||
|
||||
if (i > 0)
|
||||
selection.append(" OR ");
|
||||
|
||||
selection.append(Images.URL + " = ?");
|
||||
selectionArgs[i] = url;
|
||||
}
|
||||
|
||||
return cr.query(mImagesUriWithProfile,
|
||||
new String[] { Images.URL, Images.THUMBNAIL },
|
||||
selection.toString(),
|
||||
selectionArgs,
|
||||
null);
|
||||
}
|
||||
|
||||
public void removeThumbnails(ContentResolver cr) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.putNull(Images.THUMBNAIL);
|
||||
|
|
|
@ -16,4 +16,5 @@ public interface InputConnectionHandler
|
|||
boolean onKeyLongPress(int keyCode, KeyEvent event);
|
||||
boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event);
|
||||
boolean onKeyUp(int keyCode, KeyEvent event);
|
||||
boolean isIMEEnabled();
|
||||
}
|
||||
|
|
|
@ -261,6 +261,13 @@ public class LayerView extends FrameLayout {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean isIMEEnabled() {
|
||||
if (mInputConnectionHandler != null) {
|
||||
return mInputConnectionHandler.isIMEEnabled();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void requestRender() {
|
||||
if (mListener != null) {
|
||||
mListener.renderRequested();
|
||||
|
|
|
@ -218,7 +218,7 @@
|
|||
<item name="android:paddingBottom">@dimen/abouthome_icon_radius</item>
|
||||
<item name="android:paddingLeft">0dip</item>
|
||||
<item name="android:paddingRight">0dip</item>
|
||||
<item name="android:scaleType">centerCrop</item>
|
||||
<item name="android:scaleType">fitCenter</item>
|
||||
</style>
|
||||
|
||||
<style name="AboutHome.Thumbnail.Label">
|
||||
|
|
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.mozilla.gecko.util;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Static library version of {@link android.util.LruCache}. Used to write apps
|
||||
* that run on API levels prior to 12. When running on API level 12 or above,
|
||||
* this implementation is still used; it does not try to switch to the
|
||||
* framework's implementation. See the framework SDK documentation for a class
|
||||
* overview.
|
||||
*/
|
||||
public class LruCache<K, V> {
|
||||
private final LinkedHashMap<K, V> map;
|
||||
|
||||
/** Size of this cache in units. Not necessarily the number of elements. */
|
||||
private int size;
|
||||
private int maxSize;
|
||||
|
||||
private int putCount;
|
||||
private int createCount;
|
||||
private int evictionCount;
|
||||
private int hitCount;
|
||||
private int missCount;
|
||||
|
||||
/**
|
||||
* @param maxSize for caches that do not override {@link #sizeOf}, this is
|
||||
* the maximum number of entries in the cache. For all other caches,
|
||||
* this is the maximum sum of the sizes of the entries in this cache.
|
||||
*/
|
||||
public LruCache(int maxSize) {
|
||||
if (maxSize <= 0) {
|
||||
throw new IllegalArgumentException("maxSize <= 0");
|
||||
}
|
||||
this.maxSize = maxSize;
|
||||
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for {@code key} if it exists in the cache or can be
|
||||
* created by {@code #create}. If a value was returned, it is moved to the
|
||||
* head of the queue. This returns null if a value is not cached and cannot
|
||||
* be created.
|
||||
*/
|
||||
public final V get(K key) {
|
||||
if (key == null) {
|
||||
throw new NullPointerException("key == null");
|
||||
}
|
||||
|
||||
V mapValue;
|
||||
synchronized (this) {
|
||||
mapValue = map.get(key);
|
||||
if (mapValue != null) {
|
||||
hitCount++;
|
||||
return mapValue;
|
||||
}
|
||||
missCount++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to create a value. This may take a long time, and the map
|
||||
* may be different when create() returns. If a conflicting value was
|
||||
* added to the map while create() was working, we leave that value in
|
||||
* the map and release the created value.
|
||||
*/
|
||||
|
||||
V createdValue = create(key);
|
||||
if (createdValue == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
createCount++;
|
||||
mapValue = map.put(key, createdValue);
|
||||
|
||||
if (mapValue != null) {
|
||||
// There was a conflict so undo that last put
|
||||
map.put(key, mapValue);
|
||||
} else {
|
||||
size += safeSizeOf(key, createdValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (mapValue != null) {
|
||||
entryRemoved(false, key, createdValue, mapValue);
|
||||
return mapValue;
|
||||
} else {
|
||||
trimToSize(maxSize);
|
||||
return createdValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches {@code value} for {@code key}. The value is moved to the head of
|
||||
* the queue.
|
||||
*
|
||||
* @return the previous value mapped by {@code key}.
|
||||
*/
|
||||
public final V put(K key, V value) {
|
||||
if (key == null || value == null) {
|
||||
throw new NullPointerException("key == null || value == null");
|
||||
}
|
||||
|
||||
V previous;
|
||||
synchronized (this) {
|
||||
putCount++;
|
||||
size += safeSizeOf(key, value);
|
||||
previous = map.put(key, value);
|
||||
if (previous != null) {
|
||||
size -= safeSizeOf(key, previous);
|
||||
}
|
||||
}
|
||||
|
||||
if (previous != null) {
|
||||
entryRemoved(false, key, previous, value);
|
||||
}
|
||||
|
||||
trimToSize(maxSize);
|
||||
return previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxSize the maximum size of the cache before returning. May be -1
|
||||
* to evict even 0-sized elements.
|
||||
*/
|
||||
private void trimToSize(int maxSize) {
|
||||
while (true) {
|
||||
K key;
|
||||
V value;
|
||||
synchronized (this) {
|
||||
if (size < 0 || (map.isEmpty() && size != 0)) {
|
||||
throw new IllegalStateException(getClass().getName()
|
||||
+ ".sizeOf() is reporting inconsistent results!");
|
||||
}
|
||||
|
||||
if (size <= maxSize || map.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
|
||||
key = toEvict.getKey();
|
||||
value = toEvict.getValue();
|
||||
map.remove(key);
|
||||
size -= safeSizeOf(key, value);
|
||||
evictionCount++;
|
||||
}
|
||||
|
||||
entryRemoved(true, key, value, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the entry for {@code key} if it exists.
|
||||
*
|
||||
* @return the previous value mapped by {@code key}.
|
||||
*/
|
||||
public final V remove(K key) {
|
||||
if (key == null) {
|
||||
throw new NullPointerException("key == null");
|
||||
}
|
||||
|
||||
V previous;
|
||||
synchronized (this) {
|
||||
previous = map.remove(key);
|
||||
if (previous != null) {
|
||||
size -= safeSizeOf(key, previous);
|
||||
}
|
||||
}
|
||||
|
||||
if (previous != null) {
|
||||
entryRemoved(false, key, previous, null);
|
||||
}
|
||||
|
||||
return previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for entries that have been evicted or removed. This method is
|
||||
* invoked when a value is evicted to make space, removed by a call to
|
||||
* {@link #remove}, or replaced by a call to {@link #put}. The default
|
||||
* implementation does nothing.
|
||||
*
|
||||
* <p>The method is called without synchronization: other threads may
|
||||
* access the cache while this method is executing.
|
||||
*
|
||||
* @param evicted true if the entry is being removed to make space, false
|
||||
* if the removal was caused by a {@link #put} or {@link #remove}.
|
||||
* @param newValue the new value for {@code key}, if it exists. If non-null,
|
||||
* this removal was caused by a {@link #put}. Otherwise it was caused by
|
||||
* an eviction or a {@link #remove}.
|
||||
*/
|
||||
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
|
||||
|
||||
/**
|
||||
* Called after a cache miss to compute a value for the corresponding key.
|
||||
* Returns the computed value or null if no value can be computed. The
|
||||
* default implementation returns null.
|
||||
*
|
||||
* <p>The method is called without synchronization: other threads may
|
||||
* access the cache while this method is executing.
|
||||
*
|
||||
* <p>If a value for {@code key} exists in the cache when this method
|
||||
* returns, the created value will be released with {@link #entryRemoved}
|
||||
* and discarded. This can occur when multiple threads request the same key
|
||||
* at the same time (causing multiple values to be created), or when one
|
||||
* thread calls {@link #put} while another is creating a value for the same
|
||||
* key.
|
||||
*/
|
||||
protected V create(K key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private int safeSizeOf(K key, V value) {
|
||||
int result = sizeOf(key, value);
|
||||
if (result < 0) {
|
||||
throw new IllegalStateException("Negative size: " + key + "=" + value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the entry for {@code key} and {@code value} in
|
||||
* user-defined units. The default implementation returns 1 so that size
|
||||
* is the number of entries and max size is the maximum number of entries.
|
||||
*
|
||||
* <p>An entry's size must not change while it is in the cache.
|
||||
*/
|
||||
protected int sizeOf(K key, V value) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache, calling {@link #entryRemoved} on each removed entry.
|
||||
*/
|
||||
public final void evictAll() {
|
||||
trimToSize(-1); // -1 will evict 0-sized elements
|
||||
}
|
||||
|
||||
/**
|
||||
* For caches that do not override {@link #sizeOf}, this returns the number
|
||||
* of entries in the cache. For all other caches, this returns the sum of
|
||||
* the sizes of the entries in this cache.
|
||||
*/
|
||||
public synchronized final int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* For caches that do not override {@link #sizeOf}, this returns the maximum
|
||||
* number of entries in the cache. For all other caches, this returns the
|
||||
* maximum sum of the sizes of the entries in this cache.
|
||||
*/
|
||||
public synchronized final int maxSize() {
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of times {@link #get} returned a value.
|
||||
*/
|
||||
public synchronized final int hitCount() {
|
||||
return hitCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of times {@link #get} returned null or required a new
|
||||
* value to be created.
|
||||
*/
|
||||
public synchronized final int missCount() {
|
||||
return missCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of times {@link #create(Object)} returned a value.
|
||||
*/
|
||||
public synchronized final int createCount() {
|
||||
return createCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of times {@link #put} was called.
|
||||
*/
|
||||
public synchronized final int putCount() {
|
||||
return putCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of values that have been evicted.
|
||||
*/
|
||||
public synchronized final int evictionCount() {
|
||||
return evictionCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the current contents of the cache, ordered from least
|
||||
* recently accessed to most recently accessed.
|
||||
*/
|
||||
public synchronized final Map<K, V> snapshot() {
|
||||
return new LinkedHashMap<K, V>(map);
|
||||
}
|
||||
|
||||
@Override public synchronized final String toString() {
|
||||
int accesses = hitCount + missCount;
|
||||
int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
|
||||
return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
|
||||
maxSize, hitCount, missCount, hitPercent);
|
||||
}
|
||||
}
|
|
@ -6586,6 +6586,10 @@ var ActivityObserver = {
|
|||
let isForeground = false
|
||||
switch (aTopic) {
|
||||
case "application-background" :
|
||||
let doc = BrowserApp.selectedTab.browser.contentDocument;
|
||||
if (doc.mozFullScreen) {
|
||||
doc.mozCancelFullScreen();
|
||||
}
|
||||
isForeground = false;
|
||||
break;
|
||||
case "application-foreground" :
|
||||
|
|
|
@ -52,20 +52,11 @@ SessionStore.prototype = {
|
|||
// Get file references
|
||||
this._sessionFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
this._sessionFileBackup = this._sessionFile.clone();
|
||||
this._sessionCache = this._sessionFile.clone();
|
||||
this._sessionFile.append("sessionstore.js");
|
||||
this._sessionFileBackup.append("sessionstore.bak");
|
||||
this._sessionCache.append("sessionstoreCache");
|
||||
|
||||
this._loadState = STATE_STOPPED;
|
||||
|
||||
try {
|
||||
if (!this._sessionCache.exists() || !this._sessionCache.isDirectory())
|
||||
this._sessionCache.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex); // file was write-locked?
|
||||
}
|
||||
|
||||
this._interval = Services.prefs.getIntPref("browser.sessionstore.interval");
|
||||
this._maxTabsUndo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
|
||||
|
||||
|
@ -88,36 +79,6 @@ SessionStore.prototype = {
|
|||
} catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
|
||||
}
|
||||
|
||||
this._clearCache();
|
||||
},
|
||||
|
||||
_clearCache: function ss_clearCache() {
|
||||
// First, let's get a list of files we think should be active
|
||||
let activeFiles = [];
|
||||
this._forEachBrowserWindow(function(aWindow) {
|
||||
let tabs = aWindow.BrowserApp.tabs;
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
let browser = tabs[i].browser;
|
||||
if (browser.__SS_extdata && "thumbnail" in browser.__SS_extdata)
|
||||
activeFiles.push(browser.__SS_extdata.thumbnail);
|
||||
}
|
||||
});
|
||||
|
||||
// Now, let's find the stale files in the cache folder
|
||||
let staleFiles = [];
|
||||
let cacheFiles = this._sessionCache.directoryEntries;
|
||||
while (cacheFiles.hasMoreElements()) {
|
||||
let file = cacheFiles.getNext().QueryInterface(Ci.nsILocalFile);
|
||||
let fileURI = Services.io.newFileURI(file);
|
||||
if (activeFiles.indexOf(fileURI) == -1)
|
||||
staleFiles.push(file);
|
||||
}
|
||||
|
||||
// Remove the stale files in a separate step to keep the enumerator from
|
||||
// messing up if we remove the files as we collect them.
|
||||
staleFiles.forEach(function(aFile) {
|
||||
aFile.remove(false);
|
||||
})
|
||||
},
|
||||
|
||||
_sendMessageToJava: function (aMsg) {
|
||||
|
@ -928,26 +889,6 @@ SessionStore.prototype = {
|
|||
setTabValue: function ss_setTabValue(aTab, aKey, aStringValue) {
|
||||
let browser = aTab.browser;
|
||||
|
||||
// Thumbnails are actually stored in the cache, so do the save and update the URI
|
||||
if (aKey == "thumbnail") {
|
||||
let file = this._sessionCache.clone();
|
||||
file.append("thumbnail-" + browser.contentWindowId);
|
||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
|
||||
|
||||
let source = Services.io.newURI(aStringValue, "UTF8", null);
|
||||
let target = Services.io.newFileURI(file)
|
||||
|
||||
let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist);
|
||||
persist.persistFlags = Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES | Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
|
||||
let privacyContext = browser.contentWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsILoadContext);
|
||||
persist.saveURI(source, null, null, null, null, file, privacyContext);
|
||||
|
||||
aStringValue = target.spec;
|
||||
}
|
||||
|
||||
if (!browser.__SS_extdata)
|
||||
browser.__SS_extdata = {};
|
||||
browser.__SS_extdata[aKey] = aStringValue;
|
||||
|
@ -978,7 +919,6 @@ SessionStore.prototype = {
|
|||
}
|
||||
|
||||
function notifyObservers(aMessage) {
|
||||
self._clearCache();
|
||||
Services.obs.notifyObservers(null, "sessionstore-windows-restored", aMessage || "");
|
||||
}
|
||||
|
||||
|
|
|
@ -3629,6 +3629,10 @@ pref("webgl.prefer-16bpp", false);
|
|||
pref("webgl.default-no-alpha", false);
|
||||
pref("webgl.force-layers-readback", false);
|
||||
|
||||
// Stagefright prefs
|
||||
pref("stagefright.force-enabled", false);
|
||||
pref("stagefright.disabled", false);
|
||||
|
||||
#ifdef XP_WIN
|
||||
// The default TCP send window on Windows is too small, and autotuning only occurs on receive
|
||||
pref("network.tcp.sendbuffer", 131072);
|
||||
|
|
|
@ -405,6 +405,11 @@ nsHttpHandler::IsAcceptableEncoding(const char *enc)
|
|||
if (!PL_strncasecmp(enc, "x-", 2))
|
||||
enc += 2;
|
||||
|
||||
// gzip and deflate are inherently acceptable in modern HTTP - always
|
||||
// process them if a stream converter can also be found.
|
||||
if (!PL_strcasecmp(enc, "gzip") || !PL_strcasecmp(enc, "deflate"))
|
||||
return true;
|
||||
|
||||
return nsHttp::FindToken(mAcceptEncodings.get(), enc, HTTP_LWS ",") != nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ REPOSITORY_PATHS = [
|
|||
'mozboot/bootstrap.py',
|
||||
'mozboot/centos.py',
|
||||
'mozboot/fedora.py',
|
||||
'mozboot/gentoo.py',
|
||||
'mozboot/mint.py',
|
||||
'mozboot/openbsd.py',
|
||||
'mozboot/osx.py',
|
||||
|
|
|
@ -10,6 +10,7 @@ import sys
|
|||
|
||||
from mozboot.centos import CentOSBootstrapper
|
||||
from mozboot.fedora import FedoraBootstrapper
|
||||
from mozboot.gentoo import GentooBootstrapper
|
||||
from mozboot.mint import MintBootstrapper
|
||||
from mozboot.osx import OSXBootstrapper
|
||||
from mozboot.openbsd import OpenBSDBootstrapper
|
||||
|
@ -42,6 +43,8 @@ class Bootstrapper(object):
|
|||
cls = CentOSBootstrapper
|
||||
elif distro == 'Fedora':
|
||||
cls = FedoraBootstrapper
|
||||
elif distro == 'Gentoo Base System':
|
||||
cls = GentooBootstrapper
|
||||
elif distro == 'Mint':
|
||||
cls = MintBootstrapper
|
||||
elif distro == 'Ubuntu':
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# 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/.
|
||||
|
||||
import os
|
||||
|
||||
from mozboot.base import BaseBootstrapper
|
||||
|
||||
class GentooBootstrapper(BaseBootstrapper):
|
||||
def __init__(self, version, dist_id):
|
||||
BaseBootstrapper.__init__(self)
|
||||
|
||||
self.version = version
|
||||
self.dist_id = dist_id
|
||||
|
||||
def install_system_packages(self):
|
||||
self.run_as_root(['emerge', '--onlydeps', '--quiet', 'firefox'])
|
||||
|
||||
self.run_as_root(['emerge', '--quiet', 'git', 'mercurial'])
|
|
@ -689,7 +689,9 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
|
||||
let curWindow = this.getCurrentWindow();
|
||||
let marionette = new Marionette(this, curWindow, "chrome", this.marionetteLog, this.marionettePerf);
|
||||
let marionette = new Marionette(this, curWindow, "chrome",
|
||||
this.marionetteLog, this.marionettePerf,
|
||||
this.scriptTimeout);
|
||||
let _chromeSandbox = this.createExecuteSandbox(curWindow, marionette, aRequest.args, aRequest.specialPowers);
|
||||
if (!_chromeSandbox)
|
||||
return;
|
||||
|
@ -799,7 +801,8 @@ MarionetteDriverActor.prototype = {
|
|||
let original_onerror = curWindow.onerror;
|
||||
let that = this;
|
||||
let marionette = new Marionette(this, curWindow, "chrome",
|
||||
this.marionetteLog, this.marionettePerf);
|
||||
this.marionetteLog, this.marionettePerf,
|
||||
this.scriptTimeout);
|
||||
marionette.command_id = this.command_id;
|
||||
|
||||
function chromeAsyncReturnFunc(value, status) {
|
||||
|
|
|
@ -264,7 +264,9 @@ function createExecuteContentSandbox(aWindow) {
|
|||
sandbox.__proto__ = sandbox.window;
|
||||
sandbox.testUtils = utils;
|
||||
|
||||
let marionette = new Marionette(this, aWindow, "content", marionetteLogObj, marionettePerf);
|
||||
let marionette = new Marionette(this, aWindow, "content",
|
||||
marionetteLogObj, marionettePerf,
|
||||
marionetteTimeout);
|
||||
sandbox.marionette = marionette;
|
||||
marionette.exports.forEach(function(fn) {
|
||||
try {
|
||||
|
@ -445,7 +447,6 @@ function executeWithCallback(msg, timeout) {
|
|||
asyncTestTimeoutId = curWindow.setTimeout(function() {
|
||||
sandbox.asyncComplete('timed out', 28);
|
||||
}, marionetteTimeout);
|
||||
sandbox.marionette.timeout = marionetteTimeout;
|
||||
|
||||
curWindow.addEventListener('error', function win__onerror(evt) {
|
||||
curWindow.removeEventListener('error', win__onerror, true);
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
* The Marionette object, passed to the script context.
|
||||
*/
|
||||
|
||||
function Marionette(scope, window, context, logObj, perfData) {
|
||||
function Marionette(scope, window, context, logObj, perfData, timeout) {
|
||||
this.scope = scope;
|
||||
this.window = window;
|
||||
this.tests = [];
|
||||
this.logObj = logObj;
|
||||
this.perfData = perfData;
|
||||
this.context = context;
|
||||
this.timeout = 0;
|
||||
this.timeout = timeout;
|
||||
this.TEST_UNEXPECTED_FAIL = "TEST-UNEXPECTED-FAIL";
|
||||
this.TEST_PASS = "TEST-PASS";
|
||||
this.TEST_KNOWN_FAIL = "TEST-KNOWN-FAIL";
|
||||
|
@ -144,14 +144,15 @@ Marionette.prototype = {
|
|||
callback();
|
||||
return;
|
||||
}
|
||||
timeout = timeout || Date.now();
|
||||
if (Date.now() - timeout > this.timeout) {
|
||||
var now = Date.now();
|
||||
var deadline = now + (typeof(timeout) == "undefined" ? this.timeout : timeout);
|
||||
if (deadline <= now) {
|
||||
dump("waitFor timeout: " + test.toString() + "\n");
|
||||
// the script will timeout here, so no need to raise a separate
|
||||
// timeout exception
|
||||
return;
|
||||
}
|
||||
this.window.setTimeout(this.waitFor.bind(this), 100, callback, test, timeout);
|
||||
this.window.setTimeout(this.waitFor.bind(this), 100, callback, test, deadline - now);
|
||||
},
|
||||
|
||||
runEmulatorCmd: function runEmulatorCmd(cmd, callback) {
|
||||
|
|
|
@ -96,8 +96,11 @@ Tester.prototype = {
|
|||
Services.obs.addObserver(this, "chrome-document-global-created", false);
|
||||
Services.obs.addObserver(this, "content-document-global-created", false);
|
||||
this._globalProperties = Object.keys(window);
|
||||
this._globalPropertyWhitelist = ["navigator", "constructor", "Application",
|
||||
"__SS_tabsToRestore", "__SSi", "webConsoleCommandController",
|
||||
this._globalPropertyWhitelist = [
|
||||
"navigator", "constructor", "top",
|
||||
"Application",
|
||||
"__SS_tabsToRestore", "__SSi",
|
||||
"webConsoleCommandController",
|
||||
];
|
||||
|
||||
if (this.tests.length)
|
||||
|
|
|
@ -206,13 +206,11 @@ function _parseModifiers(aEvent)
|
|||
* a mousedown followed by a mouse up is performed.
|
||||
*
|
||||
* aWindow is optional, and defaults to the current window object.
|
||||
*
|
||||
* Returns whether the event had preventDefault() called on it.
|
||||
*/
|
||||
function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
|
||||
{
|
||||
var rect = aTarget.getBoundingClientRect();
|
||||
return synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
|
||||
synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
|
||||
aEvent, aWindow);
|
||||
}
|
||||
function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
|
||||
|
@ -236,7 +234,6 @@ function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
|
|||
function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
|
||||
{
|
||||
var utils = _getDOMWindowUtils(aWindow);
|
||||
var defaultPrevented = false;
|
||||
|
||||
if (utils) {
|
||||
var button = aEvent.button || 0;
|
||||
|
@ -244,15 +241,13 @@ function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
|
|||
var modifiers = _parseModifiers(aEvent);
|
||||
|
||||
if (("type" in aEvent) && aEvent.type) {
|
||||
defaultPrevented = utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers);
|
||||
utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers);
|
||||
}
|
||||
else {
|
||||
utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers);
|
||||
utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
return defaultPrevented;
|
||||
}
|
||||
function synthesizeTouchAtPoint(left, top, aEvent, aWindow)
|
||||
{
|
||||
|
|
|
@ -264,7 +264,6 @@ REMOTE_XPCSHELL = \
|
|||
rm -f ./$@.log && \
|
||||
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
|
||||
-I$(topsrcdir)/build \
|
||||
-I$(topsrcdir)/build/mobile \
|
||||
-I$(topsrcdir)/testing/mozbase/mozdevice/mozdevice \
|
||||
$(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \
|
||||
--manifest=$(DEPTH)/_tests/xpcshell/xpcshell.ini \
|
||||
|
@ -276,6 +275,37 @@ REMOTE_XPCSHELL = \
|
|||
$(SYMBOLS_PATH) \
|
||||
$(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
|
||||
|
||||
B2G_XPCSHELL = \
|
||||
rm -f ./@.log && \
|
||||
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
|
||||
-I$(topsrcdir)/build \
|
||||
$(topsrcdir)/testing/xpcshell/runtestsb2g.py \
|
||||
--manifest=$(DEPTH)/_tests/xpcshell/xpcshell.ini \
|
||||
--build-info-json=$(DEPTH)/mozinfo.json \
|
||||
--no-logfiles \
|
||||
--use-device-libs \
|
||||
--no-clean \
|
||||
--objdir=$(DEPTH) \
|
||||
$$EXTRA_XPCSHELL_ARGS \
|
||||
--b2gpath=${B2G_HOME} \
|
||||
$(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
|
||||
|
||||
xpcshell-tests-b2g: ADB_PATH?=$(shell which adb)
|
||||
xpcshell-tests-b2g:
|
||||
@if [ "${B2G_HOME}" = "" ]; then \
|
||||
echo "Please set the B2G_HOME variable"; exit 1; \
|
||||
elif [ ! -f "${ADB_PATH}" ]; then \
|
||||
echo "Please set the ADB_PATH variable"; exit 1; \
|
||||
elif [ "${EMULATOR}" != "" ]; then \
|
||||
EXTRA_XPCSHELL_ARGS=--emulator=${EMULATOR}; \
|
||||
$(call B2G_XPCSHELL); \
|
||||
exit 0; \
|
||||
else \
|
||||
EXTRA_XPCSHELL_ARGS=--address=localhost:2828; \
|
||||
$(call B2G_XPCSHELL); \
|
||||
exit 0; \
|
||||
fi
|
||||
|
||||
xpcshell-tests-remote: DM_TRANS?=adb
|
||||
xpcshell-tests-remote:
|
||||
@if [ "${TEST_DEVICE}" != "" -o "$(DM_TRANS)" = "adb" ]; \
|
||||
|
|
|
@ -8,7 +8,7 @@ import re, sys, os
|
|||
import subprocess
|
||||
import runxpcshelltests as xpcshell
|
||||
from automationutils import *
|
||||
import devicemanager, devicemanagerADB, devicemanagerSUT
|
||||
from mozdevice import devicemanager, devicemanagerADB, devicemanagerSUT
|
||||
|
||||
# A specialization of XPCShellTests that runs tests on an Android device
|
||||
# via devicemanager.
|
||||
|
|
|
@ -9,7 +9,7 @@ import os
|
|||
import traceback
|
||||
from remotexpcshelltests import XPCShellRemote, RemoteXPCShellOptions
|
||||
from automationutils import *
|
||||
import devicemanagerADB
|
||||
from mozdevice import devicemanagerADB
|
||||
|
||||
DEVICE_TEST_ROOT = '/data/local/tests'
|
||||
|
||||
|
|
|
@ -2,18 +2,12 @@
|
|||
* 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/. */
|
||||
|
||||
#ifdef XP_WIN
|
||||
#define USE_HITTEST
|
||||
#elifdef MOZ_WIDGET_COCOA
|
||||
#define USE_HITTEST
|
||||
#endif
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "WindowDraggingElement" ];
|
||||
|
||||
this.WindowDraggingElement = function WindowDraggingElement(elem) {
|
||||
this._elem = elem;
|
||||
this._window = elem.ownerDocument.defaultView;
|
||||
#ifdef USE_HITTEST
|
||||
#ifdef XP_WIN
|
||||
if (!this.isPanel())
|
||||
this._elem.addEventListener("MozMouseHittest", this, false);
|
||||
else
|
||||
|
@ -60,7 +54,7 @@ WindowDraggingElement.prototype = {
|
|||
},
|
||||
handleEvent: function(aEvent) {
|
||||
let isPanel = this.isPanel();
|
||||
#ifdef USE_HITTEST
|
||||
#ifdef XP_WIN
|
||||
if (!isPanel) {
|
||||
if (this.shouldDrag(aEvent))
|
||||
aEvent.preventDefault();
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
<statusbar id="statusbar">
|
||||
<statusbarpanel>
|
||||
<label id="statuslabel" value="Status"/>
|
||||
<label id="statuslabelnodrag" value="No Drag" onmousedown="event.preventDefault()"/>
|
||||
</statusbarpanel>
|
||||
</statusbar>
|
||||
|
||||
|
@ -105,18 +106,26 @@ function test_titlebar()
|
|||
var titlebar = document.getElementById("titlebar");
|
||||
var label = document.getElementById("label");
|
||||
|
||||
// On Mac, the window can also be moved with the statusbar, but this works
|
||||
// via the MozMouseHittest event, not via mouse events.
|
||||
// on Mac, the window can also be moved with the statusbar
|
||||
if (navigator.platform.indexOf("Mac") >= 0) {
|
||||
var preventDefaulted;
|
||||
|
||||
var statuslabel = document.getElementById("statuslabel");
|
||||
preventDefaulted = synthesizeMouse(statuslabel, 2, 2, { type: "MozMouseHittest" });
|
||||
SimpleTest.ok(preventDefaulted, "MozMouseHittest should have been defaultPrevented over statusbar");
|
||||
var statuslabelnodrag = document.getElementById("statuslabelnodrag");
|
||||
|
||||
var button = document.getElementById("button");
|
||||
preventDefaulted = synthesizeMouse(button, 2, 2, { type: "MozMouseHittest" });
|
||||
SimpleTest.ok(!preventDefaulted, "MozMouseHittest should NOT have been defaultPrevented over button");
|
||||
origoldx = window.screenX;
|
||||
origoldy = window.screenY;
|
||||
|
||||
synthesizeMouse(statuslabel, 2, 2, { type: "mousedown" });
|
||||
synthesizeMouse(statuslabel, 22, 22, { type: "mousemove" });
|
||||
SimpleTest.is(window.screenX, origoldx + 20, "move window with statusbar horizontal");
|
||||
SimpleTest.is(window.screenY, origoldy + 20, "move window with statusbar vertical");
|
||||
synthesizeMouse(statuslabel, 22, 22, { type: "mouseup" });
|
||||
|
||||
// event was cancelled so the drag should not have occurred
|
||||
synthesizeMouse(statuslabelnodrag, 2, 2, { type: "mousedown" });
|
||||
synthesizeMouse(statuslabelnodrag, 22, 22, { type: "mousemove" });
|
||||
SimpleTest.is(window.screenX, origoldx + 20, "move window with statusbar cancelled mousedown horizontal");
|
||||
SimpleTest.is(window.screenY, origoldy + 20, "move window with statusbar cancelled mousedown vertical");
|
||||
synthesizeMouse(statuslabelnodrag, 22, 22, { type: "mouseup" });
|
||||
}
|
||||
|
||||
origoldx = window.screenX;
|
||||
|
|
|
@ -107,7 +107,6 @@ AndroidBridge::Init(JNIEnv *jEnv,
|
|||
jEnableLocationHighAccuracy = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableLocationHighAccuracy", "(Z)V");
|
||||
jEnableSensor = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableSensor", "(I)V");
|
||||
jDisableSensor = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "disableSensor", "(I)V");
|
||||
jReturnIMEQueryResult = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "returnIMEQueryResult", "(Ljava/lang/String;II)V");
|
||||
jScheduleRestart = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "scheduleRestart", "()V");
|
||||
jNotifyXreExit = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "onXreExit", "()V");
|
||||
jGetHandlersForMimeType = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getHandlersForMimeType", "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;");
|
||||
|
@ -400,25 +399,6 @@ AndroidBridge::DisableSensor(int aSensorType)
|
|||
env->CallStaticVoidMethod(mGeckoAppShellClass, jDisableSensor, aSensorType);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::ReturnIMEQueryResult(const PRUnichar *aResult, uint32_t aLen,
|
||||
int aSelStart, int aSelLen)
|
||||
{
|
||||
ALOG_BRIDGE("AndroidBridge::ReturnIMEQueryResult");
|
||||
|
||||
JNIEnv *env = GetJNIEnv();
|
||||
if (!env)
|
||||
return;
|
||||
|
||||
AutoLocalJNIFrame jniFrame(env);
|
||||
jvalue args[3];
|
||||
args[0].l = NewJavaString(&jniFrame, aResult, aLen);
|
||||
args[1].i = aSelStart;
|
||||
args[2].i = aSelLen;
|
||||
env->CallStaticVoidMethodA(mGeckoAppShellClass,
|
||||
jReturnIMEQueryResult, args);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::ScheduleRestart()
|
||||
{
|
||||
|
|
|
@ -100,7 +100,7 @@ class AndroidBridge
|
|||
public:
|
||||
enum {
|
||||
NOTIFY_IME_RESETINPUTSTATE = 0,
|
||||
NOTIFY_IME_SETOPENSTATE = 1,
|
||||
NOTIFY_IME_REPLY_EVENT = 1,
|
||||
NOTIFY_IME_CANCELCOMPOSITION = 2,
|
||||
NOTIFY_IME_FOCUSCHANGE = 3
|
||||
};
|
||||
|
@ -171,8 +171,6 @@ public:
|
|||
|
||||
void DisableSensor(int aSensorType);
|
||||
|
||||
void ReturnIMEQueryResult(const PRUnichar *aResult, uint32_t aLen, int aSelStart, int aSelLen);
|
||||
|
||||
void NotifyXreExit();
|
||||
|
||||
void ScheduleRestart();
|
||||
|
@ -408,7 +406,6 @@ protected:
|
|||
jmethodID jEnableLocationHighAccuracy;
|
||||
jmethodID jEnableSensor;
|
||||
jmethodID jDisableSensor;
|
||||
jmethodID jReturnIMEQueryResult;
|
||||
jmethodID jNotifyAppShellReady;
|
||||
jmethodID jNotifyXreExit;
|
||||
jmethodID jScheduleRestart;
|
||||
|
|
|
@ -33,8 +33,9 @@ jfieldID AndroidGeckoEvent::jDomKeyLocationField = 0;
|
|||
jfieldID AndroidGeckoEvent::jFlagsField = 0;
|
||||
jfieldID AndroidGeckoEvent::jUnicodeCharField = 0;
|
||||
jfieldID AndroidGeckoEvent::jRepeatCountField = 0;
|
||||
jfieldID AndroidGeckoEvent::jOffsetField = 0;
|
||||
jfieldID AndroidGeckoEvent::jCountField = 0;
|
||||
jfieldID AndroidGeckoEvent::jStartField = 0;
|
||||
jfieldID AndroidGeckoEvent::jEndField = 0;
|
||||
jfieldID AndroidGeckoEvent::jPointerIndexField = 0;
|
||||
jfieldID AndroidGeckoEvent::jRangeTypeField = 0;
|
||||
jfieldID AndroidGeckoEvent::jRangeStylesField = 0;
|
||||
|
@ -227,8 +228,9 @@ AndroidGeckoEvent::InitGeckoEventClass(JNIEnv *jEnv)
|
|||
jFlagsField = getField("mFlags", "I");
|
||||
jUnicodeCharField = getField("mUnicodeChar", "I");
|
||||
jRepeatCountField = getField("mRepeatCount", "I");
|
||||
jOffsetField = getField("mOffset", "I");
|
||||
jCountField = getField("mCount", "I");
|
||||
jStartField = getField("mStart", "I");
|
||||
jEndField = getField("mEnd", "I");
|
||||
jPointerIndexField = getField("mPointerIndex", "I");
|
||||
jRangeTypeField = getField("mRangeType", "I");
|
||||
jRangeStylesField = getField("mRangeStyles", "I");
|
||||
|
@ -564,14 +566,13 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
|
|||
break;
|
||||
|
||||
case IME_EVENT:
|
||||
if (mAction == IME_GET_TEXT || mAction == IME_SET_SELECTION) {
|
||||
mOffset = jenv->GetIntField(jobj, jOffsetField);
|
||||
mCount = jenv->GetIntField(jobj, jCountField);
|
||||
} else if (mAction == IME_SET_TEXT || mAction == IME_ADD_RANGE) {
|
||||
if (mAction == IME_SET_TEXT)
|
||||
ReadCharactersField(jenv);
|
||||
mOffset = jenv->GetIntField(jobj, jOffsetField);
|
||||
mCount = jenv->GetIntField(jobj, jCountField);
|
||||
mStart = jenv->GetIntField(jobj, jStartField);
|
||||
mEnd = jenv->GetIntField(jobj, jEndField);
|
||||
|
||||
if (mAction == IME_REPLACE_TEXT) {
|
||||
ReadCharactersField(jenv);
|
||||
} else if (mAction == IME_UPDATE_COMPOSITION ||
|
||||
mAction == IME_ADD_COMPOSITION_RANGE) {
|
||||
mRangeType = jenv->GetIntField(jobj, jRangeTypeField);
|
||||
mRangeStyles = jenv->GetIntField(jobj, jRangeStylesField);
|
||||
mRangeForeColor =
|
||||
|
|
|
@ -676,8 +676,9 @@ public:
|
|||
int Flags() { return mFlags; }
|
||||
int UnicodeChar() { return mUnicodeChar; }
|
||||
int RepeatCount() const { return mRepeatCount; }
|
||||
int Offset() { return mOffset; }
|
||||
int Count() { return mCount; }
|
||||
int Start() { return mStart; }
|
||||
int End() { return mEnd; }
|
||||
int PointerIndex() { return mPointerIndex; }
|
||||
int RangeType() { return mRangeType; }
|
||||
int RangeStyles() { return mRangeStyles; }
|
||||
|
@ -703,7 +704,8 @@ protected:
|
|||
int mDomKeyLocation;
|
||||
int mKeyCode, mUnicodeChar;
|
||||
int mRepeatCount;
|
||||
int mOffset, mCount;
|
||||
int mCount;
|
||||
int mStart, mEnd;
|
||||
int mRangeType, mRangeStyles;
|
||||
int mRangeForeColor, mRangeBackColor;
|
||||
double mX, mY, mZ;
|
||||
|
@ -753,8 +755,9 @@ protected:
|
|||
static jfieldID jMetaStateField;
|
||||
static jfieldID jDomKeyLocationField;
|
||||
static jfieldID jFlagsField;
|
||||
static jfieldID jOffsetField;
|
||||
static jfieldID jCountField;
|
||||
static jfieldID jStartField;
|
||||
static jfieldID jEndField;
|
||||
static jfieldID jPointerIndexField;
|
||||
static jfieldID jUnicodeCharField;
|
||||
static jfieldID jRepeatCountField;
|
||||
|
@ -807,14 +810,12 @@ public:
|
|||
};
|
||||
|
||||
enum {
|
||||
IME_COMPOSITION_END = 0,
|
||||
IME_COMPOSITION_BEGIN = 1,
|
||||
IME_SET_TEXT = 2,
|
||||
IME_GET_TEXT = 3,
|
||||
IME_DELETE_TEXT = 4,
|
||||
IME_SET_SELECTION = 5,
|
||||
IME_GET_SELECTION = 6,
|
||||
IME_ADD_RANGE = 7
|
||||
IME_SYNCHRONIZE = 0,
|
||||
IME_REPLACE_TEXT = 1,
|
||||
IME_SET_SELECTION = 2,
|
||||
IME_ADD_COMPOSITION_RANGE = 3,
|
||||
IME_UPDATE_COMPOSITION = 4,
|
||||
IME_REMOVE_COMPOSITION = 5
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -122,27 +122,26 @@ GfxInfo::EnsureInitializedFromGfxInfoData()
|
|||
// as it's useful information that isn't given anywhere else in about:support of in crash reports.
|
||||
// But we should really move this out of GfxInfo.
|
||||
if (mozilla::AndroidBridge::Bridge()) {
|
||||
nsAutoString str;
|
||||
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MODEL", str)) {
|
||||
mAdapterDescription.AppendPrintf(" -- Model: %s", NS_LossyConvertUTF16toASCII(str).get());
|
||||
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MODEL", mModel)) {
|
||||
mAdapterDescription.AppendPrintf(" -- Model: %s", NS_LossyConvertUTF16toASCII(mModel).get());
|
||||
}
|
||||
|
||||
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "PRODUCT", str)) {
|
||||
mAdapterDescription.AppendPrintf(", Product: %s", NS_LossyConvertUTF16toASCII(str).get());
|
||||
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "PRODUCT", mProduct)) {
|
||||
mAdapterDescription.AppendPrintf(", Product: %s", NS_LossyConvertUTF16toASCII(mProduct).get());
|
||||
}
|
||||
|
||||
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MANUFACTURER", str)) {
|
||||
mAdapterDescription.AppendPrintf(", Manufacturer: %s", NS_LossyConvertUTF16toASCII(str).get());
|
||||
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MANUFACTURER", mManufacturer)) {
|
||||
mAdapterDescription.AppendPrintf(", Manufacturer: %s", NS_LossyConvertUTF16toASCII(mManufacturer).get());
|
||||
}
|
||||
|
||||
int32_t version; // the HARDWARE field isn't available on Android SDK < 8
|
||||
if (!mozilla::AndroidBridge::Bridge()->GetStaticIntField("android/os/Build$VERSION", "SDK_INT", &version))
|
||||
version = 0;
|
||||
int32_t signedVersion;
|
||||
if (!mozilla::AndroidBridge::Bridge()->GetStaticIntField("android/os/Build$VERSION", "SDK_INT", &signedVersion))
|
||||
signedVersion = 0;
|
||||
mOSVersion = signedVersion;
|
||||
|
||||
if (version >= 8 && mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "HARDWARE", str)) {
|
||||
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "HARDWARE", str)) {
|
||||
mAdapterDescription.AppendPrintf(", Hardware: %s", NS_LossyConvertUTF16toASCII(str).get());
|
||||
}
|
||||
// the HARDWARE field isn't available on Android SDK < 8
|
||||
if (mOSVersion >= 8 && mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "HARDWARE", mHardware)) {
|
||||
mAdapterDescription.AppendPrintf(", Hardware: %s", NS_LossyConvertUTF16toASCII(mHardware).get());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,7 +308,7 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
|
|||
NS_ENSURE_ARG_POINTER(aStatus);
|
||||
aSuggestedDriverVersion.SetIsVoid(true);
|
||||
*aStatus = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
|
||||
OperatingSystem os = DRIVER_OS_ANDROID;
|
||||
OperatingSystem os = mOS;
|
||||
if (aOS)
|
||||
*aOS = os;
|
||||
|
||||
|
@ -330,6 +329,27 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
|
|||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (aFeature == FEATURE_STAGEFRIGHT) {
|
||||
NS_LossyConvertUTF16toASCII cManufacturer(mManufacturer);
|
||||
NS_LossyConvertUTF16toASCII cModel(mModel);
|
||||
if (mOSVersion < 14 /* Before version 4.0 */ )
|
||||
{
|
||||
*aStatus = nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
|
||||
return NS_OK;
|
||||
}
|
||||
else if (mOSVersion < 16 /* Before version 4.1 */ )
|
||||
{
|
||||
bool isWhitelisted =
|
||||
cManufacturer.Equals("samsung", nsCaseInsensitiveCStringComparator()) ||
|
||||
cModel.Equals("galaxy nexus", nsCaseInsensitiveCStringComparator()); // some Galaxy Nexus have manufacturer=amazon
|
||||
|
||||
if (!isWhitelisted) {
|
||||
*aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GfxInfoBase::GetFeatureStatusImpl(aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, &os);
|
||||
|
@ -366,7 +386,29 @@ NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString & aDriverVersion)
|
|||
/* void spoofOSVersion (in unsigned long aVersion); */
|
||||
NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t aVersion)
|
||||
{
|
||||
EnsureInitializedFromGfxInfoData(); // initialization from GfxInfo data overwrites mOSVersion
|
||||
mOSVersion = aVersion;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const nsAString& GfxInfo::Model() const
|
||||
{
|
||||
return mModel;
|
||||
}
|
||||
|
||||
const nsAString& GfxInfo::Hardware() const
|
||||
{
|
||||
return mHardware;
|
||||
}
|
||||
|
||||
const nsAString& GfxInfo::Product() const
|
||||
{
|
||||
return mProduct;
|
||||
}
|
||||
|
||||
const nsAString& GfxInfo::Manufacturer() const
|
||||
{
|
||||
return mManufacturer;
|
||||
}
|
||||
|
|
|
@ -48,11 +48,18 @@ public:
|
|||
|
||||
void EnsureInitializedFromGfxInfoData();
|
||||
|
||||
virtual const nsAString& Model() const;
|
||||
virtual const nsAString& Hardware() const;
|
||||
virtual const nsAString& Product() const;
|
||||
virtual const nsAString& Manufacturer() const;
|
||||
|
||||
#ifdef DEBUG
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIGFXINFODEBUG
|
||||
#endif
|
||||
|
||||
virtual uint32_t OperatingSystemVersion() const { return mOSVersion; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual nsresult GetFeatureStatusImpl(int32_t aFeature,
|
||||
|
@ -76,6 +83,11 @@ private:
|
|||
nsCString mError;
|
||||
|
||||
nsCString mAdapterDescription;
|
||||
|
||||
OperatingSystem mOS;
|
||||
uint32_t mOSVersion;
|
||||
|
||||
nsString mModel, mHardware, mManufacturer, mProduct;
|
||||
};
|
||||
|
||||
} // namespace widget
|
||||
|
|
|
@ -161,7 +161,9 @@ nsWindow::nsWindow() :
|
|||
mIsVisible(false),
|
||||
mParent(nullptr),
|
||||
mFocus(nullptr),
|
||||
mIMEComposing(false)
|
||||
mIMEComposing(false),
|
||||
mIMEMaskSelectionUpdate(false),
|
||||
mIMEMaskTextUpdate(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -639,20 +641,18 @@ nsWindow::DispatchEvent(nsGUIEvent *aEvent)
|
|||
|
||||
switch (aEvent->message) {
|
||||
case NS_COMPOSITION_START:
|
||||
MOZ_ASSERT(!mIMEComposing);
|
||||
mIMEComposing = true;
|
||||
break;
|
||||
case NS_COMPOSITION_END:
|
||||
MOZ_ASSERT(mIMEComposing);
|
||||
mIMEComposing = false;
|
||||
mIMEComposingText.Truncate();
|
||||
break;
|
||||
case NS_TEXT_TEXT:
|
||||
MOZ_ASSERT(mIMEComposing);
|
||||
mIMEComposingText = static_cast<nsTextEvent*>(aEvent)->theText;
|
||||
break;
|
||||
case NS_KEY_PRESS:
|
||||
// Sometimes the text changes after a key press do not generate notifications (see Bug 723810)
|
||||
// Call the corresponding methods explicitly to send those changes back to Java
|
||||
OnIMETextChange(0, 0, 0);
|
||||
OnIMESelectionChange();
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
@ -1751,6 +1751,7 @@ void
|
|||
nsWindow::OnKeyEvent(AndroidGeckoEvent *ae)
|
||||
{
|
||||
nsRefPtr<nsWindow> kungFuDeathGrip(this);
|
||||
RemoveIMEComposition();
|
||||
uint32_t msg;
|
||||
switch (ae->Action()) {
|
||||
case AndroidKeyEvent::ACTION_DOWN:
|
||||
|
@ -1816,89 +1817,216 @@ nsWindow::OnKeyEvent(AndroidGeckoEvent *ae)
|
|||
#define ALOGIME(args...)
|
||||
#endif
|
||||
|
||||
void
|
||||
nsWindow::OnIMEAddRange(AndroidGeckoEvent *ae)
|
||||
static nscolor
|
||||
ConvertAndroidColor(uint32_t c)
|
||||
{
|
||||
//ALOGIME("IME: IME_ADD_RANGE");
|
||||
nsTextRange range;
|
||||
range.mStartOffset = ae->Offset();
|
||||
range.mEndOffset = range.mStartOffset + ae->Count();
|
||||
range.mRangeType = ae->RangeType();
|
||||
range.mRangeStyle.mDefinedStyles = ae->RangeStyles();
|
||||
range.mRangeStyle.mLineStyle = nsTextRangeStyle::LINESTYLE_SOLID;
|
||||
range.mRangeStyle.mForegroundColor = NS_RGBA(
|
||||
((ae->RangeForeColor() >> 16) & 0xff),
|
||||
((ae->RangeForeColor() >> 8) & 0xff),
|
||||
(ae->RangeForeColor() & 0xff),
|
||||
((ae->RangeForeColor() >> 24) & 0xff));
|
||||
range.mRangeStyle.mBackgroundColor = NS_RGBA(
|
||||
((ae->RangeBackColor() >> 16) & 0xff),
|
||||
((ae->RangeBackColor() >> 8) & 0xff),
|
||||
(ae->RangeBackColor() & 0xff),
|
||||
((ae->RangeBackColor() >> 24) & 0xff));
|
||||
mIMERanges.AppendElement(range);
|
||||
return;
|
||||
return NS_RGBA((c & 0x000000ff),
|
||||
(c & 0x0000ff00) >> 8,
|
||||
(c & 0x00ff0000) >> 16,
|
||||
(c & 0xff000000) >> 24);
|
||||
}
|
||||
|
||||
class AutoIMEMask {
|
||||
private:
|
||||
bool mOldMask, *mMask;
|
||||
public:
|
||||
AutoIMEMask(bool &mask) : mOldMask(mask), mMask(&mask) {
|
||||
mask = true;
|
||||
}
|
||||
~AutoIMEMask() {
|
||||
*mMask = mOldMask;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Remove the composition but leave the text content as-is
|
||||
*/
|
||||
void
|
||||
nsWindow::RemoveIMEComposition()
|
||||
{
|
||||
// Remove composition on Gecko side
|
||||
if (!mIMEComposing)
|
||||
return;
|
||||
|
||||
nsRefPtr<nsWindow> kungFuDeathGrip(this);
|
||||
AutoIMEMask selMask(mIMEMaskSelectionUpdate);
|
||||
AutoIMEMask textMask(mIMEMaskTextUpdate);
|
||||
|
||||
nsTextEvent textEvent(true, NS_TEXT_TEXT, this);
|
||||
InitEvent(textEvent, nullptr);
|
||||
textEvent.theText = mIMEComposingText;
|
||||
DispatchEvent(&textEvent);
|
||||
|
||||
nsCompositionEvent event(true, NS_COMPOSITION_END, this);
|
||||
InitEvent(event, nullptr);
|
||||
DispatchEvent(&event);
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
|
||||
{
|
||||
MOZ_ASSERT(!mIMEMaskTextUpdate);
|
||||
MOZ_ASSERT(!mIMEMaskSelectionUpdate);
|
||||
/*
|
||||
Rules for managing IME between Gecko and Java:
|
||||
|
||||
* Gecko controls the text content, and Java shadows the Gecko text
|
||||
through text updates
|
||||
* Java controls the selection, and Gecko shadows the Java selection
|
||||
through set selection events
|
||||
* Java controls the composition, and Gecko shadows the Java
|
||||
composition through update composition events
|
||||
*/
|
||||
nsRefPtr<nsWindow> kungFuDeathGrip(this);
|
||||
switch (ae->Action()) {
|
||||
case AndroidGeckoEvent::IME_COMPOSITION_END:
|
||||
case AndroidGeckoEvent::IME_SYNCHRONIZE:
|
||||
{
|
||||
ALOGIME("IME: IME_COMPOSITION_END");
|
||||
MOZ_ASSERT(mIMEComposing,
|
||||
"IME_COMPOSITION_END when we are not composing?!");
|
||||
|
||||
nsCompositionEvent event(true, NS_COMPOSITION_END, this);
|
||||
InitEvent(event, nullptr);
|
||||
event.data = mIMELastDispatchedComposingText;
|
||||
mIMELastDispatchedComposingText.Truncate();
|
||||
DispatchEvent(&event);
|
||||
AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT, 0);
|
||||
}
|
||||
return;
|
||||
case AndroidGeckoEvent::IME_COMPOSITION_BEGIN:
|
||||
break;
|
||||
case AndroidGeckoEvent::IME_REPLACE_TEXT:
|
||||
{
|
||||
ALOGIME("IME: IME_COMPOSITION_BEGIN");
|
||||
MOZ_ASSERT(!mIMEComposing,
|
||||
"IME_COMPOSITION_BEGIN when we are already composing?!");
|
||||
/*
|
||||
Replace text in Gecko thread from ae->Start() to ae->End()
|
||||
with the string ae->Characters()
|
||||
|
||||
mIMELastDispatchedComposingText.Truncate();
|
||||
nsCompositionEvent event(true, NS_COMPOSITION_START, this);
|
||||
InitEvent(event, nullptr);
|
||||
DispatchEvent(&event);
|
||||
}
|
||||
return;
|
||||
case AndroidGeckoEvent::IME_ADD_RANGE:
|
||||
{
|
||||
NS_ASSERTION(mIMEComposing,
|
||||
"IME_ADD_RANGE when we are not composing?!");
|
||||
OnIMEAddRange(ae);
|
||||
}
|
||||
return;
|
||||
case AndroidGeckoEvent::IME_SET_TEXT:
|
||||
{
|
||||
NS_ASSERTION(mIMEComposing,
|
||||
"IME_SET_TEXT when we are not composing?!");
|
||||
Selection updates are masked so the result of our temporary
|
||||
selection event is not passed on to Java
|
||||
|
||||
OnIMEAddRange(ae);
|
||||
Text updates are passed on, so the Java text can shadow the
|
||||
Gecko text
|
||||
*/
|
||||
AutoIMEMask selMask(mIMEMaskSelectionUpdate);
|
||||
RemoveIMEComposition();
|
||||
{
|
||||
nsSelectionEvent event(true, NS_SELECTION_SET, this);
|
||||
InitEvent(event, nullptr);
|
||||
event.mOffset = uint32_t(ae->Start());
|
||||
event.mLength = uint32_t(ae->End() - ae->Start());
|
||||
event.mExpandToClusterBoundary = false;
|
||||
DispatchEvent(&event);
|
||||
}
|
||||
{
|
||||
nsCompositionEvent event(true, NS_COMPOSITION_START, this);
|
||||
InitEvent(event, nullptr);
|
||||
DispatchEvent(&event);
|
||||
}
|
||||
{
|
||||
nsTextEvent event(true, NS_TEXT_TEXT, this);
|
||||
InitEvent(event, nullptr);
|
||||
event.theText = ae->Characters();
|
||||
DispatchEvent(&event);
|
||||
}
|
||||
{
|
||||
nsCompositionEvent event(true, NS_COMPOSITION_END, this);
|
||||
InitEvent(event, nullptr);
|
||||
event.data = ae->Characters();
|
||||
DispatchEvent(&event);
|
||||
}
|
||||
AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT, 0);
|
||||
}
|
||||
break;
|
||||
case AndroidGeckoEvent::IME_SET_SELECTION:
|
||||
{
|
||||
/*
|
||||
Set Gecko selection to ae->Start() to ae->End()
|
||||
|
||||
Selection updates are masked to prevent Java from being
|
||||
notified of the new selection
|
||||
*/
|
||||
AutoIMEMask selMask(mIMEMaskSelectionUpdate);
|
||||
nsSelectionEvent selEvent(true, NS_SELECTION_SET, this);
|
||||
InitEvent(selEvent, nullptr);
|
||||
|
||||
int32_t start = ae->Start(), end = ae->End();
|
||||
|
||||
if (start < 0 || end < 0) {
|
||||
nsQueryContentEvent event(true, NS_QUERY_SELECTED_TEXT, this);
|
||||
InitEvent(event, nullptr);
|
||||
DispatchEvent(&event);
|
||||
MOZ_ASSERT(event.mSucceeded && !event.mWasAsync);
|
||||
|
||||
if (start < 0)
|
||||
start = int32_t(event.GetSelectionStart());
|
||||
if (end < 0)
|
||||
end = int32_t(event.GetSelectionEnd());
|
||||
}
|
||||
|
||||
selEvent.mOffset = std::min(start, end);
|
||||
selEvent.mLength = std::max(start, end) - selEvent.mOffset;
|
||||
selEvent.mReversed = start > end;
|
||||
selEvent.mExpandToClusterBoundary = false;
|
||||
|
||||
DispatchEvent(&selEvent);
|
||||
}
|
||||
break;
|
||||
case AndroidGeckoEvent::IME_ADD_COMPOSITION_RANGE:
|
||||
{
|
||||
nsTextRange range;
|
||||
range.mStartOffset = ae->Start();
|
||||
range.mEndOffset = ae->End();
|
||||
range.mRangeType = ae->RangeType();
|
||||
range.mRangeStyle.mDefinedStyles = ae->RangeStyles();
|
||||
range.mRangeStyle.mLineStyle = nsTextRangeStyle::LINESTYLE_SOLID;
|
||||
range.mRangeStyle.mForegroundColor =
|
||||
ConvertAndroidColor(uint32_t(ae->RangeForeColor()));
|
||||
range.mRangeStyle.mBackgroundColor =
|
||||
ConvertAndroidColor(uint32_t(ae->RangeBackColor()));
|
||||
mIMERanges.AppendElement(range);
|
||||
}
|
||||
break;
|
||||
case AndroidGeckoEvent::IME_UPDATE_COMPOSITION:
|
||||
{
|
||||
/*
|
||||
Update the composition from ae->Start() to ae->End() using
|
||||
information from added ranges. This is only used for
|
||||
visual indication and does not affect the text content.
|
||||
Only the offsets are specified and not the text content
|
||||
to eliminate the possibility of this event altering the
|
||||
text content unintentionally.
|
||||
|
||||
Selection and text updates are masked so the result of
|
||||
temporary events are not passed on to Java
|
||||
*/
|
||||
AutoIMEMask selMask(mIMEMaskSelectionUpdate);
|
||||
AutoIMEMask textMask(mIMEMaskTextUpdate);
|
||||
RemoveIMEComposition();
|
||||
|
||||
nsTextEvent event(true, NS_TEXT_TEXT, this);
|
||||
InitEvent(event, nullptr);
|
||||
|
||||
event.theText.Assign(ae->Characters());
|
||||
event.rangeArray = mIMERanges.Elements();
|
||||
event.rangeCount = mIMERanges.Length();
|
||||
|
||||
{
|
||||
nsSelectionEvent event(true, NS_SELECTION_SET, this);
|
||||
InitEvent(event, nullptr);
|
||||
event.mOffset = uint32_t(ae->Start());
|
||||
event.mLength = uint32_t(ae->End() - ae->Start());
|
||||
event.mExpandToClusterBoundary = false;
|
||||
DispatchEvent(&event);
|
||||
}
|
||||
{
|
||||
nsQueryContentEvent queryEvent(true,
|
||||
NS_QUERY_SELECTED_TEXT, this);
|
||||
InitEvent(queryEvent, nullptr);
|
||||
DispatchEvent(&queryEvent);
|
||||
MOZ_ASSERT(queryEvent.mSucceeded && !queryEvent.mWasAsync);
|
||||
event.theText = queryEvent.mReply.mString;
|
||||
}
|
||||
{
|
||||
nsCompositionEvent event(true, NS_COMPOSITION_START, this);
|
||||
InitEvent(event, nullptr);
|
||||
DispatchEvent(&event);
|
||||
}
|
||||
|
||||
if (mIMEComposing &&
|
||||
event.theText != mIMELastDispatchedComposingText) {
|
||||
event.theText != mIMEComposingText) {
|
||||
nsCompositionEvent compositionUpdate(true,
|
||||
NS_COMPOSITION_UPDATE,
|
||||
this);
|
||||
InitEvent(compositionUpdate, nullptr);
|
||||
compositionUpdate.data = event.theText;
|
||||
mIMELastDispatchedComposingText = event.theText;
|
||||
DispatchEvent(&compositionUpdate);
|
||||
if (Destroyed())
|
||||
return;
|
||||
|
@ -1914,83 +2042,13 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
|
|||
DispatchEvent(&event);
|
||||
mIMERanges.Clear();
|
||||
}
|
||||
return;
|
||||
case AndroidGeckoEvent::IME_GET_TEXT:
|
||||
break;
|
||||
case AndroidGeckoEvent::IME_REMOVE_COMPOSITION:
|
||||
{
|
||||
ALOGIME("IME: IME_GET_TEXT: o=%u, l=%u", ae->Offset(), ae->Count());
|
||||
|
||||
nsQueryContentEvent event(true, NS_QUERY_TEXT_CONTENT, this);
|
||||
InitEvent(event, nullptr);
|
||||
|
||||
event.InitForQueryTextContent(ae->Offset(), ae->Count());
|
||||
|
||||
DispatchEvent(&event);
|
||||
|
||||
if (!event.mSucceeded) {
|
||||
ALOGIME("IME: -> failed");
|
||||
AndroidBridge::Bridge()->ReturnIMEQueryResult(
|
||||
nullptr, 0, 0, 0);
|
||||
return;
|
||||
} else if (!event.mWasAsync) {
|
||||
AndroidBridge::Bridge()->ReturnIMEQueryResult(
|
||||
event.mReply.mString.get(),
|
||||
event.mReply.mString.Length(), 0, 0);
|
||||
}
|
||||
RemoveIMEComposition();
|
||||
mIMERanges.Clear();
|
||||
}
|
||||
return;
|
||||
case AndroidGeckoEvent::IME_DELETE_TEXT:
|
||||
{
|
||||
ALOGIME("IME: IME_DELETE_TEXT");
|
||||
NS_ASSERTION(mIMEComposing,
|
||||
"IME_DELETE_TEXT when we are not composing?!");
|
||||
|
||||
nsKeyEvent event(true, NS_KEY_PRESS, this);
|
||||
ANPEvent pluginEvent;
|
||||
InitKeyEvent(event, *ae, &pluginEvent);
|
||||
event.keyCode = NS_VK_BACK;
|
||||
DispatchEvent(&event);
|
||||
}
|
||||
return;
|
||||
case AndroidGeckoEvent::IME_SET_SELECTION:
|
||||
{
|
||||
ALOGIME("IME: IME_SET_SELECTION: o=%u, l=%d", ae->Offset(), ae->Count());
|
||||
|
||||
nsSelectionEvent selEvent(true, NS_SELECTION_SET, this);
|
||||
InitEvent(selEvent, nullptr);
|
||||
|
||||
selEvent.mOffset = uint32_t(ae->Count() >= 0 ?
|
||||
ae->Offset() :
|
||||
ae->Offset() + ae->Count());
|
||||
selEvent.mLength = uint32_t(NS_ABS(ae->Count()));
|
||||
selEvent.mReversed = ae->Count() >= 0 ? false : true;
|
||||
selEvent.mExpandToClusterBoundary = false;
|
||||
|
||||
DispatchEvent(&selEvent);
|
||||
}
|
||||
return;
|
||||
case AndroidGeckoEvent::IME_GET_SELECTION:
|
||||
{
|
||||
ALOGIME("IME: IME_GET_SELECTION");
|
||||
|
||||
nsQueryContentEvent event(true, NS_QUERY_SELECTED_TEXT, this);
|
||||
InitEvent(event, nullptr);
|
||||
DispatchEvent(&event);
|
||||
|
||||
if (!event.mSucceeded) {
|
||||
ALOGIME("IME: -> failed");
|
||||
AndroidBridge::Bridge()->ReturnIMEQueryResult(
|
||||
nullptr, 0, 0, 0);
|
||||
return;
|
||||
} else if (!event.mWasAsync) {
|
||||
AndroidBridge::Bridge()->ReturnIMEQueryResult(
|
||||
event.mReply.mString.get(),
|
||||
event.mReply.mString.Length(),
|
||||
event.GetSelectionStart(),
|
||||
event.GetSelectionEnd() - event.GetSelectionStart());
|
||||
}
|
||||
//ALOGIME("IME: -> o=%u, l=%u", event.mReply.mOffset, event.mReply.mString.Length());
|
||||
}
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2027,28 +2085,8 @@ NS_IMETHODIMP
|
|||
nsWindow::ResetInputState()
|
||||
{
|
||||
//ALOGIME("IME: ResetInputState: s=%d", aState);
|
||||
|
||||
// Cancel composition on Gecko side
|
||||
if (mIMEComposing) {
|
||||
nsRefPtr<nsWindow> kungFuDeathGrip(this);
|
||||
|
||||
nsTextEvent textEvent(true, NS_TEXT_TEXT, this);
|
||||
InitEvent(textEvent, nullptr);
|
||||
textEvent.theText = mIMEComposingText;
|
||||
DispatchEvent(&textEvent);
|
||||
mIMEComposingText.Truncate(0);
|
||||
|
||||
nsCompositionEvent event(true, NS_COMPOSITION_END, this);
|
||||
InitEvent(event, nullptr);
|
||||
DispatchEvent(&event);
|
||||
}
|
||||
|
||||
RemoveIMEComposition();
|
||||
AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_RESETINPUTSTATE, 0);
|
||||
|
||||
// Send IME text/selection change notifications
|
||||
OnIMETextChange(0, 0, 0);
|
||||
OnIMESelectionChange();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2108,7 +2146,6 @@ nsWindow::CancelIMEComposition()
|
|||
nsTextEvent textEvent(true, NS_TEXT_TEXT, this);
|
||||
InitEvent(textEvent, nullptr);
|
||||
DispatchEvent(&textEvent);
|
||||
mIMEComposingText.Truncate(0);
|
||||
|
||||
nsCompositionEvent compEvent(true, NS_COMPOSITION_END, this);
|
||||
InitEvent(compEvent, nullptr);
|
||||
|
@ -2128,7 +2165,7 @@ nsWindow::OnIMEFocusChange(bool aFocus)
|
|||
int(aFocus));
|
||||
|
||||
if (aFocus) {
|
||||
OnIMETextChange(0, 0, 0);
|
||||
OnIMETextChange(0, INT32_MAX, INT32_MAX);
|
||||
OnIMESelectionChange();
|
||||
}
|
||||
|
||||
|
@ -2138,22 +2175,16 @@ nsWindow::OnIMEFocusChange(bool aFocus)
|
|||
NS_IMETHODIMP
|
||||
nsWindow::OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd)
|
||||
{
|
||||
if (mIMEMaskTextUpdate)
|
||||
return NS_OK;
|
||||
|
||||
ALOGIME("IME: OnIMETextChange: s=%d, oe=%d, ne=%d",
|
||||
aStart, aOldEnd, aNewEnd);
|
||||
|
||||
if (!mInputContext.mIMEState.mEnabled) {
|
||||
AndroidBridge::NotifyIMEChange(nullptr, 0, 0, 0, 0);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// A quirk in Android makes it necessary to pass the whole text.
|
||||
// The more efficient way would have been passing the substring from index
|
||||
// aStart to index aNewEnd
|
||||
|
||||
nsRefPtr<nsWindow> kungFuDeathGrip(this);
|
||||
nsQueryContentEvent event(true, NS_QUERY_TEXT_CONTENT, this);
|
||||
InitEvent(event, nullptr);
|
||||
event.InitForQueryTextContent(0, UINT32_MAX);
|
||||
event.InitForQueryTextContent(aStart, aNewEnd - aStart);
|
||||
|
||||
DispatchEvent(&event);
|
||||
if (!event.mSucceeded)
|
||||
|
@ -2163,18 +2194,18 @@ nsWindow::OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd)
|
|||
event.mReply.mString.Length(),
|
||||
aStart, aOldEnd, aNewEnd);
|
||||
|
||||
/* Make sure Java's selection is up-to-date */
|
||||
OnIMESelectionChange();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindow::OnIMESelectionChange(void)
|
||||
{
|
||||
ALOGIME("IME: OnIMESelectionChange");
|
||||
|
||||
if (!mInputContext.mIMEState.mEnabled) {
|
||||
AndroidBridge::NotifyIMEChange(nullptr, 0, 0, 0, -1);
|
||||
if (mIMEMaskSelectionUpdate)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ALOGIME("IME: OnIMESelectionChange");
|
||||
|
||||
nsRefPtr<nsWindow> kungFuDeathGrip(this);
|
||||
nsQueryContentEvent event(true, NS_QUERY_SELECTED_TEXT, this);
|
||||
|
@ -2184,9 +2215,8 @@ nsWindow::OnIMESelectionChange(void)
|
|||
if (!event.mSucceeded)
|
||||
return NS_OK;
|
||||
|
||||
AndroidBridge::NotifyIMEChange(nullptr, 0, int(event.mReply.mOffset),
|
||||
int(event.mReply.mOffset +
|
||||
event.mReply.mString.Length()), -1);
|
||||
AndroidBridge::NotifyIMEChange(nullptr, 0, int(event.GetSelectionStart()),
|
||||
int(event.GetSelectionEnd()), -1);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ protected:
|
|||
bool DrawTo(gfxASurface *targetSurface);
|
||||
bool DrawTo(gfxASurface *targetSurface, const nsIntRect &aRect);
|
||||
bool IsTopLevel();
|
||||
void OnIMEAddRange(mozilla::AndroidGeckoEvent *ae);
|
||||
void RemoveIMEComposition();
|
||||
|
||||
// Call this function when the users activity is the direct cause of an
|
||||
// event (like a keypress or mouse click).
|
||||
|
@ -183,8 +183,8 @@ protected:
|
|||
nsCOMPtr<nsIIdleServiceInternal> mIdleService;
|
||||
|
||||
bool mIMEComposing;
|
||||
bool mIMEMaskSelectionUpdate, mIMEMaskTextUpdate;
|
||||
nsString mIMEComposingText;
|
||||
nsString mIMELastDispatchedComposingText;
|
||||
nsAutoTArray<nsTextRange, 4> mIMERanges;
|
||||
|
||||
InputContext mInputContext;
|
||||
|
|
|
@ -53,6 +53,8 @@ public:
|
|||
NS_DECL_NSIGFXINFODEBUG
|
||||
#endif
|
||||
|
||||
virtual uint32_t OperatingSystemVersion() const { return mOSXVersion; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual nsresult GetFeatureStatusImpl(int32_t aFeature,
|
||||
|
|
|
@ -183,10 +183,6 @@ typedef NSInteger NSEventGestureAxis;
|
|||
- (NSEventPhase)momentumPhase;
|
||||
@end
|
||||
|
||||
@protocol EventRedirection
|
||||
- (NSView*)targetView;
|
||||
@end
|
||||
|
||||
@interface ChildView : NSView<
|
||||
#ifdef ACCESSIBILITY
|
||||
mozAccessible,
|
||||
|
@ -271,8 +267,6 @@ typedef NSInteger NSEventGestureAxis;
|
|||
// class initialization
|
||||
+ (void)initialize;
|
||||
|
||||
+ (void)registerViewForDraggedTypes:(NSView*)aView;
|
||||
|
||||
// these are sent to the first responder when the window key status changes
|
||||
- (void)viewsWindowDidBecomeKey;
|
||||
- (void)viewsWindowDidResignKey;
|
||||
|
@ -282,8 +276,6 @@ typedef NSInteger NSEventGestureAxis;
|
|||
|
||||
- (void)sendFocusEvent:(uint32_t)eventType;
|
||||
|
||||
- (void)updateWindowDraggableStateOnMouseMove:(NSEvent*)theEvent;
|
||||
|
||||
- (void)handleMouseMoved:(NSEvent*)aEvent;
|
||||
|
||||
- (void)drawRect:(NSRect)aRect inTitlebarContext:(CGContextRef)aContext;
|
||||
|
|
|
@ -1912,20 +1912,6 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
|||
}
|
||||
}
|
||||
|
||||
+ (void)registerViewForDraggedTypes:(NSView*)aView
|
||||
{
|
||||
[aView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
|
||||
NSStringPboardType,
|
||||
NSHTMLPboardType,
|
||||
NSURLPboardType,
|
||||
NSFilesPromisePboardType,
|
||||
kWildcardPboardType,
|
||||
kCorePboardType_url,
|
||||
kCorePboardType_urld,
|
||||
kCorePboardType_urln,
|
||||
nil]];
|
||||
}
|
||||
|
||||
// initWithFrame:geckoChild:
|
||||
- (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild
|
||||
{
|
||||
|
@ -1973,8 +1959,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
|||
}
|
||||
|
||||
// register for things we'll take from other applications
|
||||
[ChildView registerViewForDraggedTypes:self];
|
||||
|
||||
PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView initWithFrame: registering drag types\n"));
|
||||
[self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
|
||||
NSStringPboardType,
|
||||
NSHTMLPboardType,
|
||||
NSURLPboardType,
|
||||
NSFilesPromisePboardType,
|
||||
kWildcardPboardType,
|
||||
kCorePboardType_url,
|
||||
kCorePboardType_urld,
|
||||
kCorePboardType_urln,
|
||||
nil]];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(windowBecameMain:)
|
||||
name:NSWindowDidBecomeMainNotification
|
||||
|
@ -2344,7 +2339,7 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
|||
|
||||
- (BOOL)mouseDownCanMoveWindow
|
||||
{
|
||||
return [[self window] isMovableByWindowBackground];
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)lockFocus
|
||||
|
@ -3290,25 +3285,6 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
|||
mGeckoChild->DispatchEvent(&event, status);
|
||||
}
|
||||
|
||||
- (void)updateWindowDraggableStateOnMouseMove:(NSEvent*)theEvent
|
||||
{
|
||||
if (!theEvent || !mGeckoChild) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCocoaWindow* windowWidget = mGeckoChild->GetXULWindowWidget();
|
||||
if (!windowWidget) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We assume later on that sending a hit test event won't cause widget destruction.
|
||||
nsMouseEvent hitTestEvent(true, NS_MOUSE_MOZHITTEST, mGeckoChild, nsMouseEvent::eReal);
|
||||
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&hitTestEvent];
|
||||
bool result = mGeckoChild->DispatchWindowEvent(hitTestEvent);
|
||||
|
||||
[windowWidget->GetCocoaWindow() setMovableByWindowBackground:result];
|
||||
}
|
||||
|
||||
- (void)handleMouseMoved:(NSEvent*)theEvent
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
@ -4833,11 +4809,6 @@ ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
|
|||
|
||||
NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, window);
|
||||
NSView* view = [[[window contentView] superview] hitTest:windowEventLocation];
|
||||
|
||||
while([view conformsToProtocol:@protocol(EventRedirection)]) {
|
||||
view = [(id<EventRedirection>)view targetView];
|
||||
}
|
||||
|
||||
if (![view isKindOfClass:[ChildView class]])
|
||||
return nil;
|
||||
|
||||
|
@ -4936,7 +4907,7 @@ ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
|
|||
NSWindow *ourWindow = [self window];
|
||||
NSView *contentView = [ourWindow contentView];
|
||||
if ([ourWindow isKindOfClass:[ToolbarWindow class]] && (self == contentView))
|
||||
return [ourWindow isMovableByWindowBackground];
|
||||
return NO;
|
||||
return [self nsChildView_NSView_mouseDownCanMoveWindow];
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
class nsCocoaWindow;
|
||||
class nsChildView;
|
||||
class nsMenuBarX;
|
||||
@class ChildView;
|
||||
|
||||
// Value copied from BITMAP_MAX_AREA, used in nsNativeThemeCocoa.mm
|
||||
#define CUIDRAW_MAX_AREA 500000
|
||||
|
@ -177,7 +176,6 @@ typedef struct _nsCocoaWindowList {
|
|||
TitlebarAndBackgroundColor *mColor;
|
||||
float mUnifiedToolbarHeight;
|
||||
NSColor *mBackgroundColor;
|
||||
NSView *mTitlebarView; // strong
|
||||
}
|
||||
// Pass nil here to get the default appearance.
|
||||
- (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive;
|
||||
|
@ -188,7 +186,6 @@ typedef struct _nsCocoaWindowList {
|
|||
- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect sync:(BOOL)aSync;
|
||||
- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect;
|
||||
- (void)setDrawsContentsIntoWindowFrame:(BOOL)aState;
|
||||
- (ChildView*)mainChildView;
|
||||
@end
|
||||
|
||||
class nsCocoaWindow : public nsBaseWidget, public nsPIWidgetCocoa
|
||||
|
|
|
@ -447,10 +447,6 @@ nsresult nsCocoaWindow::CreateNativeWindow(const NSRect &aRect,
|
|||
[mWindow setContentMinSize:NSMakeSize(60, 60)];
|
||||
[mWindow disableCursorRects];
|
||||
|
||||
// Make sure the window starts out not draggable by the background.
|
||||
// We will turn it on as necessary.
|
||||
[mWindow setMovableByWindowBackground:NO];
|
||||
|
||||
[[WindowDataMap sharedWindowDataMap] ensureDataForWindow:mWindow];
|
||||
mWindowMadeHere = true;
|
||||
|
||||
|
@ -2587,94 +2583,7 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
|
|||
|
||||
@end
|
||||
|
||||
@interface TitlebarMouseHandlingView : NSView<EventRedirection>
|
||||
{
|
||||
ToolbarWindow* mWindow; // weak
|
||||
BOOL mProcessingRightMouseDown;
|
||||
}
|
||||
|
||||
- (id)initWithWindow:(ToolbarWindow*)aWindow;
|
||||
@end
|
||||
|
||||
@implementation TitlebarMouseHandlingView
|
||||
|
||||
- (id)initWithWindow:(ToolbarWindow*)aWindow
|
||||
{
|
||||
if ((self = [super initWithFrame:[aWindow titlebarRect]])) {
|
||||
mWindow = aWindow;
|
||||
[self setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
|
||||
[ChildView registerViewForDraggedTypes:self];
|
||||
mProcessingRightMouseDown = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSView*)targetView
|
||||
{
|
||||
return [mWindow mainChildView];
|
||||
}
|
||||
|
||||
- (BOOL)mouseDownCanMoveWindow
|
||||
{
|
||||
return [mWindow isMovableByWindowBackground];
|
||||
}
|
||||
|
||||
// We redirect many types of events to the window's mainChildView simply by
|
||||
// passing the event object to the respective handler method. We don't need any
|
||||
// coordinate transformations because event coordinates are relative to the
|
||||
// window.
|
||||
// We only need to handle event types whose target NSView is determined by the
|
||||
// event's position. We don't need to handle key events and NSMouseMoved events
|
||||
// because those are only sent to the window's first responder. This view
|
||||
// doesn't override acceptsFirstResponder, so it will never receive those kinds
|
||||
// of events.
|
||||
|
||||
- (void)mouseMoved:(NSEvent*)aEvent { [[self targetView] mouseMoved:aEvent]; }
|
||||
- (void)mouseDown:(NSEvent*)aEvent { [[self targetView] mouseDown:aEvent]; }
|
||||
- (void)mouseUp:(NSEvent*)aEvent { [[self targetView] mouseUp:aEvent]; }
|
||||
- (void)mouseDragged:(NSEvent*)aEvent { [[self targetView] mouseDragged:aEvent]; }
|
||||
- (void)rightMouseDown:(NSEvent*)aEvent
|
||||
{
|
||||
// To avoid recursion...
|
||||
if (mProcessingRightMouseDown)
|
||||
return;
|
||||
mProcessingRightMouseDown = YES;
|
||||
[[self targetView] rightMouseDown:aEvent];
|
||||
mProcessingRightMouseDown = NO;
|
||||
}
|
||||
- (void)rightMouseUp:(NSEvent*)aEvent { [[self targetView] rightMouseUp:aEvent]; }
|
||||
- (void)rightMouseDragged:(NSEvent*)aEvent { [[self targetView] rightMouseDragged:aEvent]; }
|
||||
- (void)otherMouseDown:(NSEvent*)aEvent { [[self targetView] otherMouseDown:aEvent]; }
|
||||
- (void)otherMouseUp:(NSEvent*)aEvent { [[self targetView] otherMouseUp:aEvent]; }
|
||||
- (void)otherMouseDragged:(NSEvent*)aEvent { [[self targetView] otherMouseDragged:aEvent]; }
|
||||
- (void)scrollWheel:(NSEvent*)aEvent { [[self targetView] scrollWheel:aEvent]; }
|
||||
- (void)swipeWithEvent:(NSEvent*)aEvent { [[self targetView] swipeWithEvent:aEvent]; }
|
||||
- (void)beginGestureWithEvent:(NSEvent*)aEvent { [[self targetView] beginGestureWithEvent:aEvent]; }
|
||||
- (void)magnifyWithEvent:(NSEvent*)aEvent { [[self targetView] magnifyWithEvent:aEvent]; }
|
||||
- (void)rotateWithEvent:(NSEvent*)aEvent { [[self targetView] rotateWithEvent:aEvent]; }
|
||||
- (void)endGestureWithEvent:(NSEvent*)aEvent { [[self targetView] endGestureWithEvent:aEvent]; }
|
||||
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
|
||||
{ return [[self targetView] draggingEntered:sender]; }
|
||||
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
|
||||
{ return [[self targetView] draggingUpdated:sender]; }
|
||||
- (void)draggingExited:(id <NSDraggingInfo>)sender
|
||||
{ [[self targetView] draggingExited:sender]; }
|
||||
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
|
||||
{ return [[self targetView] performDragOperation:sender]; }
|
||||
- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
|
||||
{ [[self targetView] draggedImage:anImage endedAt:aPoint operation:operation]; }
|
||||
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
|
||||
{ return [[self targetView] draggingSourceOperationMaskForLocal:isLocal]; }
|
||||
- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDestination
|
||||
{ return [[self targetView] namesOfPromisedFilesDroppedAtDestination:dropDestination]; }
|
||||
- (NSMenu*)menuForEvent:(NSEvent*)aEvent
|
||||
{ return [[self targetView] menuForEvent:aEvent]; }
|
||||
|
||||
@end
|
||||
|
||||
// This class allows us to exercise control over the window's title bar. This
|
||||
// allows for a "unified toolbar" look, and for extending the content area into
|
||||
// the title bar. It works like this:
|
||||
// This class allows us to have a "unified toolbar" style window. It works like this:
|
||||
// 1) We set the window's style to textured.
|
||||
// 2) Because of this, the background color applies to the entire window, including
|
||||
// the titlebar area. For normal textured windows, the default pattern is a
|
||||
|
@ -2708,11 +2617,6 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
|
|||
// to the containing window - the other direction doesn't work. That's why the
|
||||
// toolbar height is cached in the ToolbarWindow but nsNativeThemeCocoa can simply
|
||||
// query the window for its titlebar height when drawing the toolbar.
|
||||
@interface ToolbarWindow(Private)
|
||||
- (void)installTitlebarMouseHandlingView;
|
||||
- (void)uninstallTitlebarMouseHandlingView;
|
||||
@end;
|
||||
|
||||
@implementation ToolbarWindow
|
||||
|
||||
- (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
|
||||
|
@ -2747,7 +2651,6 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
|
|||
|
||||
[mColor release];
|
||||
[mBackgroundColor release];
|
||||
[mTitlebarView release];
|
||||
[super dealloc];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
|
@ -2827,48 +2730,11 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
|
|||
[self setTitlebarNeedsDisplayInRect:[self titlebarRect] sync:needSyncRedraw];
|
||||
}
|
||||
|
||||
// Extending the content area into the title bar works by redirection of both
|
||||
// drawing and mouse events.
|
||||
// The window's NSView hierarchy looks like this:
|
||||
// - border view ([[window contentView] superview])
|
||||
// - transparent title bar event redirection view
|
||||
// - window controls (traffic light buttons)
|
||||
// - content view ([window contentView], default NSView provided by the window)
|
||||
// - our main Gecko ChildView ([window mainChildView]), which has an
|
||||
// OpenGL context attached to it when accelerated
|
||||
// - possibly more ChildViews for plugins
|
||||
//
|
||||
// When the window is in title bar extension mode, the mainChildView covers the
|
||||
// whole window but is only visible in the content area of the window, because
|
||||
// it's a subview of the window's contentView and thus clipped to its dimensions.
|
||||
// This clipping is a good thing because it avoids a few problems. For example,
|
||||
// if the mainChildView weren't clipped and thus visible in the titlebar, we'd
|
||||
// have have to do the rounded corner masking and the drawing of the highlight
|
||||
// line ourselves.
|
||||
// This would be especially hard in combination with OpenGL acceleration since
|
||||
// rounded corners would require making the OpenGL context transparent, which
|
||||
// would bring another set of challenges with it. Having the window controls
|
||||
// draw on top of an OpenGL context could be hard, too.
|
||||
//
|
||||
// So title bar drawing happens in the border view. The border view's drawRect
|
||||
// method is not under our control, but we can get it to call into our code
|
||||
// using some tricks, see the TitlebarAndBackgroundColor class below.
|
||||
// Specifically, we have it call the TitlebarDrawCallback function, which
|
||||
// draws the contents of mainChildView into the provided CGContext.
|
||||
// (Even if the ChildView uses OpenGL for rendering, drawing in the title bar
|
||||
// will happen non-accelerated in that CGContext.)
|
||||
//
|
||||
// Mouse event redirection happens via a TitlebarMouseHandlingView which we
|
||||
// install below.
|
||||
- (void)setDrawsContentsIntoWindowFrame:(BOOL)aState
|
||||
{
|
||||
BOOL stateChanged = ([self drawsContentsIntoWindowFrame] != aState);
|
||||
[super setDrawsContentsIntoWindowFrame:aState];
|
||||
if (stateChanged && [[self delegate] isKindOfClass:[WindowDelegate class]]) {
|
||||
// Here we extend / shrink our mainChildView. We do that by firing a resize
|
||||
// event which will cause the ChildView to be resized to the rect returned
|
||||
// by nsCocoaWindow::GetClientBounds. GetClientBounds bases its return
|
||||
// value on what we return from drawsContentsIntoWindowFrame.
|
||||
WindowDelegate *windowDelegate = (WindowDelegate *)[self delegate];
|
||||
nsCocoaWindow *geckoWindow = [windowDelegate geckoWidget];
|
||||
if (geckoWindow) {
|
||||
|
@ -2883,35 +2749,10 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
|
|||
// we'll send a mouse move event with the correct new position.
|
||||
ChildViewMouseTracker::ResendLastMouseMoveEvent();
|
||||
|
||||
if (aState) {
|
||||
[self installTitlebarMouseHandlingView];
|
||||
} else {
|
||||
[self uninstallTitlebarMouseHandlingView];
|
||||
}
|
||||
[self setTitlebarNeedsDisplayInRect:[self titlebarRect]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)installTitlebarMouseHandlingView
|
||||
{
|
||||
mTitlebarView = [[TitlebarMouseHandlingView alloc] initWithWindow:self];
|
||||
[[[self contentView] superview] addSubview:mTitlebarView positioned:NSWindowBelow relativeTo:nil];
|
||||
}
|
||||
|
||||
- (void)uninstallTitlebarMouseHandlingView
|
||||
{
|
||||
[mTitlebarView removeFromSuperview];
|
||||
[mTitlebarView release];
|
||||
mTitlebarView = nil;
|
||||
}
|
||||
|
||||
- (ChildView*)mainChildView
|
||||
{
|
||||
NSView* view = [[[self contentView] subviews] lastObject];
|
||||
if (view && [view isKindOfClass:[ChildView class]])
|
||||
return (ChildView*)view;
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Returning YES here makes the setShowsToolbarButton method work even though
|
||||
// the window doesn't contain an NSToolbar.
|
||||
- (BOOL)_hasToolbar
|
||||
|
@ -2980,9 +2821,6 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
|
|||
if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
|
||||
nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget];
|
||||
if (widget) {
|
||||
if (type == NSMouseMoved) {
|
||||
[[self mainChildView] updateWindowDraggableStateOnMouseMove:anEvent];
|
||||
}
|
||||
if (gGeckoAppModalWindowList && (widget != gGeckoAppModalWindowList->window))
|
||||
return;
|
||||
if (widget->HasModalDescendents())
|
||||
|
@ -3048,8 +2886,8 @@ TitlebarDrawCallback(void* aInfo, CGContextRef aContext)
|
|||
NSRect titlebarRect = [window titlebarRect];
|
||||
|
||||
if ([window drawsContentsIntoWindowFrame]) {
|
||||
ChildView* view = [window mainChildView];
|
||||
if (!view)
|
||||
NSView* view = [[[window contentView] subviews] lastObject];
|
||||
if (!view || ![view isKindOfClass:[ChildView class]])
|
||||
return;
|
||||
|
||||
// Gecko drawing assumes flippedness, but the current context isn't flipped
|
||||
|
@ -3060,7 +2898,7 @@ TitlebarDrawCallback(void* aInfo, CGContextRef aContext)
|
|||
CGContextTranslateCTM(aContext, 0.0f, -[window frame].size.height);
|
||||
|
||||
NSRect flippedTitlebarRect = { NSZeroPoint, titlebarRect.size };
|
||||
[view drawRect:flippedTitlebarRect inTitlebarContext:aContext];
|
||||
[(ChildView*)view drawRect:flippedTitlebarRect inTitlebarContext:aContext];
|
||||
} else {
|
||||
BOOL isMain = [window isMainWindow];
|
||||
NSColor *titlebarColor = [window titlebarColorForActiveWindow:isMain];
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
/* NOTE: this interface is completely undesigned, not stable and likely to change */
|
||||
|
||||
[scriptable, uuid(a67c77af-2952-4028-93ab-e7bc3b43cf81)]
|
||||
[scriptable, uuid(8a9797ae-22d4-431d-a628-18fd5900c53c)]
|
||||
interface nsIGfxInfo : nsISupports
|
||||
{
|
||||
/*
|
||||
|
@ -77,6 +77,8 @@ interface nsIGfxInfo : nsISupports
|
|||
const long FEATURE_WEBGL_ANGLE = 7;
|
||||
/* Whether WebGL antialiasing is supported. */
|
||||
const long FEATURE_WEBGL_MSAA = 8;
|
||||
/* Whether Stagefright is supported */
|
||||
const long FEATURE_STAGEFRIGHT = 9;
|
||||
|
||||
/*
|
||||
* A set of return values from GetFeatureStatus
|
||||
|
|
|
@ -45,6 +45,8 @@ public:
|
|||
|
||||
virtual nsresult Init();
|
||||
|
||||
virtual uint32_t OperatingSystemVersion() const { return mWindowsVersion; }
|
||||
|
||||
#ifdef DEBUG
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIGFXINFODEBUG
|
||||
|
|
|
@ -17,6 +17,7 @@ nsAString* GfxDriverInfo::mDeviceVendors[DeviceVendorMax];
|
|||
|
||||
GfxDriverInfo::GfxDriverInfo()
|
||||
: mOperatingSystem(DRIVER_OS_UNKNOWN),
|
||||
mOperatingSystemVersion(0),
|
||||
mAdapterVendor(GfxDriverInfo::GetDeviceVendor(VendorAll)),
|
||||
mDevices(allDevices),
|
||||
mDeleteDevices(false),
|
||||
|
@ -36,6 +37,7 @@ GfxDriverInfo::GfxDriverInfo(OperatingSystem os, nsAString& vendor,
|
|||
const char *suggestedVersion /* = nullptr */,
|
||||
bool ownDevices /* = false */)
|
||||
: mOperatingSystem(os),
|
||||
mOperatingSystemVersion(0),
|
||||
mAdapterVendor(vendor),
|
||||
mDevices(devices),
|
||||
mDeleteDevices(ownDevices),
|
||||
|
@ -49,6 +51,7 @@ GfxDriverInfo::GfxDriverInfo(OperatingSystem os, nsAString& vendor,
|
|||
|
||||
GfxDriverInfo::GfxDriverInfo(const GfxDriverInfo& aOrig)
|
||||
: mOperatingSystem(aOrig.mOperatingSystem),
|
||||
mOperatingSystemVersion(aOrig.mOperatingSystemVersion),
|
||||
mAdapterVendor(aOrig.mAdapterVendor),
|
||||
mFeature(aOrig.mFeature),
|
||||
mFeatureStatus(aOrig.mFeatureStatus),
|
||||
|
|
|
@ -86,6 +86,7 @@ struct GfxDriverInfo
|
|||
~GfxDriverInfo();
|
||||
|
||||
OperatingSystem mOperatingSystem;
|
||||
uint32_t mOperatingSystemVersion;
|
||||
|
||||
nsString mAdapterVendor;
|
||||
|
||||
|
@ -117,6 +118,8 @@ struct GfxDriverInfo
|
|||
|
||||
static const nsAString& GetDeviceVendor(DeviceVendor id);
|
||||
static nsAString* mDeviceVendors[DeviceVendorMax];
|
||||
|
||||
nsString mModel, mHardware, mProduct, mManufacturer;
|
||||
};
|
||||
|
||||
#define GFX_DRIVER_VERSION(a,b,c,d) \
|
||||
|
|
|
@ -121,6 +121,9 @@ GetPrefNameForFeature(int32_t aFeature)
|
|||
case nsIGfxInfo::FEATURE_WEBGL_MSAA:
|
||||
name = BLACKLIST_PREF_BRANCH "webgl.msaa";
|
||||
break;
|
||||
case nsIGfxInfo::FEATURE_STAGEFRIGHT:
|
||||
name = BLACKLIST_PREF_BRANCH "stagefright";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
@ -270,7 +273,8 @@ BlacklistFeatureToGfxFeature(const nsAString& aFeature)
|
|||
return nsIGfxInfo::FEATURE_WEBGL_ANGLE;
|
||||
else if (aFeature == NS_LITERAL_STRING("WEBGL_MSAA"))
|
||||
return nsIGfxInfo::FEATURE_WEBGL_MSAA;
|
||||
|
||||
else if (aFeature == NS_LITERAL_STRING("STAGEFRIGHT"))
|
||||
return nsIGfxInfo::FEATURE_STAGEFRIGHT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -379,6 +383,13 @@ BlacklistEntryToDriverInfo(nsIDOMNode* aBlacklistEntry,
|
|||
aDriverInfo.mOperatingSystem = BlacklistOSToOperatingSystem(dataValue);
|
||||
}
|
||||
|
||||
// <osversion>14</osversion> currently only used for Android
|
||||
if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("osversion"),
|
||||
getter_AddRefs(dataNode))) {
|
||||
BlacklistNodeToTextValue(dataNode, dataValue);
|
||||
aDriverInfo.mOperatingSystemVersion = strtoul(NS_LossyConvertUTF16toASCII(dataValue).get(), NULL, 10);
|
||||
}
|
||||
|
||||
// <vendor>0x8086</vendor>
|
||||
if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("vendor"),
|
||||
getter_AddRefs(dataNode))) {
|
||||
|
@ -440,6 +451,31 @@ BlacklistEntryToDriverInfo(nsIDOMNode* aBlacklistEntry,
|
|||
aDriverInfo.mComparisonOp = BlacklistComparatorToComparisonOp(dataValue);
|
||||
}
|
||||
|
||||
// <model>foo</model>
|
||||
if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("model"),
|
||||
getter_AddRefs(dataNode))) {
|
||||
BlacklistNodeToTextValue(dataNode, dataValue);
|
||||
aDriverInfo.mModel = dataValue;
|
||||
}
|
||||
// <product>foo</product>
|
||||
if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("product"),
|
||||
getter_AddRefs(dataNode))) {
|
||||
BlacklistNodeToTextValue(dataNode, dataValue);
|
||||
aDriverInfo.mProduct = dataValue;
|
||||
}
|
||||
// <manufacturer>foo</manufacturer>
|
||||
if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("manufacturer"),
|
||||
getter_AddRefs(dataNode))) {
|
||||
BlacklistNodeToTextValue(dataNode, dataValue);
|
||||
aDriverInfo.mManufacturer = dataValue;
|
||||
}
|
||||
// <hardware>foo</hardware>
|
||||
if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("hardware"),
|
||||
getter_AddRefs(dataNode))) {
|
||||
BlacklistNodeToTextValue(dataNode, dataValue);
|
||||
aDriverInfo.mHardware = dataValue;
|
||||
}
|
||||
|
||||
// We explicitly ignore unknown elements.
|
||||
|
||||
return true;
|
||||
|
@ -555,6 +591,10 @@ GfxInfoBase::FindBlocklistedDeviceInList(const nsTArray<GfxDriverInfo>& info,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (info[i].mOperatingSystemVersion && info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!info[i].mAdapterVendor.Equals(GfxDriverInfo::GetDeviceVendor(VendorAll), nsCaseInsensitiveStringComparator()) &&
|
||||
!info[i].mAdapterVendor.Equals(adapterVendorID, nsCaseInsensitiveStringComparator())) {
|
||||
continue;
|
||||
|
@ -576,6 +616,19 @@ GfxInfoBase::FindBlocklistedDeviceInList(const nsTArray<GfxDriverInfo>& info,
|
|||
|
||||
bool match = false;
|
||||
|
||||
if (!info[i].mHardware.IsEmpty() && !info[i].mHardware.Equals(Hardware())) {
|
||||
continue;
|
||||
}
|
||||
if (!info[i].mModel.IsEmpty() && !info[i].mModel.Equals(Model())) {
|
||||
continue;
|
||||
}
|
||||
if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) {
|
||||
continue;
|
||||
}
|
||||
if (!info[i].mManufacturer.IsEmpty() && !info[i].mManufacturer.Equals(Manufacturer())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if defined(XP_WIN) || defined(ANDROID)
|
||||
switch (info[i].mComparisonOp) {
|
||||
case DRIVER_LESS_THAN:
|
||||
|
@ -741,6 +794,7 @@ GfxInfoBase::EvaluateDownloadedBlacklist(nsTArray<GfxDriverInfo>& aDriverInfo)
|
|||
nsIGfxInfo::FEATURE_WEBGL_OPENGL,
|
||||
nsIGfxInfo::FEATURE_WEBGL_ANGLE,
|
||||
nsIGfxInfo::FEATURE_WEBGL_MSAA,
|
||||
nsIGfxInfo::FEATURE_STAGEFRIGHT,
|
||||
0
|
||||
};
|
||||
|
||||
|
|
|
@ -68,6 +68,12 @@ public:
|
|||
static nsTArray<GfxDriverInfo>* mDriverInfo;
|
||||
static bool mDriverInfoObserverInitialized;
|
||||
|
||||
virtual const nsAString& Model() const { return nsString(); }
|
||||
virtual const nsAString& Hardware() const { return nsString(); }
|
||||
virtual const nsAString& Product() const { return nsString(); }
|
||||
virtual const nsAString& Manufacturer() const { return nsString(); }
|
||||
virtual uint32_t OperatingSystemVersion() const { return 0; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual nsresult GetFeatureStatusImpl(int32_t aFeature, int32_t* aStatus,
|
||||
|
|
Загрузка…
Ссылка в новой задаче