Permalink
Browse files

Bug 1289687 - Support Internationalized Domain Names (#2019)

  • Loading branch information...
lemonYLX authored and thebnich committed Sep 1, 2016
1 parent fdc3684 commit 78be54586c9e027465d8e0da28bb42595d4ebe32
@@ -657,6 +657,9 @@
F8708D2C1A0970B40051AB07 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F8708D221A0970990051AB07 /* MainInterface.storyboard */; };
F8708D2E1A0970B70051AB07 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F8708D251A0970990051AB07 /* Images.xcassets */; };
F8708D321A0970B70051AB07 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8708D291A0970990051AB07 /* ShareViewController.swift */; };
FA6B2AC21D41F02D00429414 /* Punycode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA6B2AC11D41F02D00429414 /* Punycode.swift */; };
FA6B2AC31D41F02D00429414 /* Punycode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA6B2AC11D41F02D00429414 /* Punycode.swift */; };
FA6B2AC41D41F02D00429414 /* Punycode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA6B2AC11D41F02D00429414 /* Punycode.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -1728,6 +1731,7 @@
F8708D251A0970990051AB07 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
F8708D261A0970990051AB07 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
F8708D291A0970990051AB07 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
FA6B2AC11D41F02D00429414 /* Punycode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Punycode.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -2742,6 +2746,7 @@
D3C744CC1A687D6C004CE85D /* URIFixup.swift */,
0BF0DB931A8545800039F300 /* URLBarView.swift */,
E4ECD0871B70FD4F00B90D22 /* WindowCloseHelper.swift */,
FA6B2AC11D41F02D00429414 /* Punycode.swift */,
);
path = Browser;
sourceTree = "<group>";
@@ -4751,6 +4756,7 @@
0BF0DB941A8545800039F300 /* URLBarView.swift in Sources */,
E65607611C08B4E200534B02 /* SearchInputView.swift in Sources */,
0B3E7DB81B27A7F600E2E84D /* AboutUtils.swift in Sources */,
FA6B2AC21D41F02D00429414 /* Punycode.swift in Sources */,
7BC68CC71CC152B70043562A /* MenuItemImageView.swift in Sources */,
D301AAEE1A3A55B70078DD1D /* TabTrayController.swift in Sources */,
0B3E7D951B27A7CE00E2E84D /* AboutHomeHandler.swift in Sources */,
@@ -4941,6 +4947,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
FA6B2AC41D41F02D00429414 /* Punycode.swift in Sources */,
D38B2D8A1A8D98D00040E6B5 /* SearchEngines.swift in Sources */,
D3C744CF1A687D6C004CE85D /* URIFixup.swift in Sources */,
D38B2D8C1A8D98D90040E6B5 /* OpenSearch.swift in Sources */,
@@ -4963,6 +4970,7 @@
E4BCAAB91B0537E300855D82 /* InstructionsViewController.swift in Sources */,
E42CCDE81A23A73D00B794D3 /* Profile.swift in Sources */,
E4BCAAD91B05380B00855D82 /* ClientPickerViewController.swift in Sources */,
FA6B2AC31D41F02D00429414 /* Punycode.swift in Sources */,
F8708D2A1A0970B40051AB07 /* ActionViewController.swift in Sources */,
28CDA5641A43C37D005C318C /* NSUserDefaultsPrefs.swift in Sources */,
);
@@ -0,0 +1,198 @@
/* 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 Foundation
private let base = 36
private let tMin = 1
private let tMax = 26
private let initialBias = 72
private let initialN: Int = 128 // 0x80
private let delimiter: Character = "-"; // '\x2D'
private let prefixPunycode = "xn--"
private let asciiPunycode = [Character]("abcdefghijklmnopqrstuvwxyz0123456789".characters)
extension String {
private func toValue(index: Int) -> Character {
return asciiPunycode[index]
}
private func toIndex(value: Character) -> Int {
return asciiPunycode.indexOf(value)!
}
private func adapt(delta: Int, numPoints: Int, firstTime: Bool) -> Int {
let skew = 38
let damp = firstTime ? 700 : 2
var delta = delta
delta = delta / damp
delta += delta / numPoints
var k = 0
while (delta > ((base - tMin) * tMax) / 2) {
delta /= (base - tMin)
k += base
}
return k + ((base - tMin + 1) * delta) / (delta + skew)
}
private func encode(input: String) -> String {
var output = ""
var d : Int = 0
var extendedChars = [Int]()
for c in input.unicodeScalars {
if Int(c.value) < initialN {
d += 1
output.append(c)
} else {
extendedChars.append(Int(c.value))
}
}
if extendedChars.count == 0 {
return output
}
if d > 0 {
output.append(delimiter)
}
var n = initialN
var delta = 0
var bias = initialBias
var h : Int = 0
var b : Int = 0
if d > 0 {
h = output.unicodeScalars.count - 1
b = output.unicodeScalars.count - 1
} else {
h = output.unicodeScalars.count
b = output.unicodeScalars.count
}
while h < input.unicodeScalars.count {
var char = Int(0x7fffffff)
for c in input.unicodeScalars {
let ci = Int(c.value)
if char > ci && ci >= n {
char = ci
}
}
delta = delta + (char - n) * (h + 1)
if delta < 0 {
print("error: invalid char:")
output = ""
return output
}
n = char
for c in input.unicodeScalars {
let ci = Int(c.value)
if ci < n || ci < initialN {
delta += 1
continue
}
if ci > n {
continue
}
var q = delta
var k = base
while true {
let t = max(min(k - bias, tMax), tMin)
if q < t {
break
}
let code = t + ((q - t) % (base - t))
output.append(toValue(code))
q = (q - t) / (base - t)
k += base
}
output.append(toValue(q))
bias = self.adapt(delta, numPoints: h + 1, firstTime: h == b)
delta = 0
h += 1
}
delta += 1
n += 1
}
return output
}
private func decode(punycode: String) -> String {
var input = [Character](punycode.characters)
var output = [Character]()
var i = 0
var n = initialN
var bias = initialBias
var pos = 0
if let ipos = input.indexOf(delimiter) {
pos = ipos
output.appendContentsOf(input[0 ..< pos])
pos += 1
}
var outputLength = output.count
let inputLength = input.count
while pos < inputLength {
let oldi = i
var w = 1
var k = base
while true {
let digit = toIndex(input[pos])
pos += 1
i += digit * w
let t = max(min(k - bias, tMax), tMin)
if digit < t {
break
}
w = w * (base - t)
k += base
}
outputLength += 1
bias = adapt(i - oldi, numPoints: outputLength, firstTime: (oldi == 0))
n = n + i / outputLength
i = i % outputLength
output.insert(Character(UnicodeScalar(n)), atIndex: i)
i += 1
}
return String(output)
}
private func isValidUnicodeScala(s: String) -> Bool {
for c in s.unicodeScalars {
let ci = Int(c.value)
if ci >= initialN {
return false
}
}
return true
}
private func isValidPunycodeScala(s: String) -> Bool {
return s.hasPrefix(prefixPunycode)
}
public func utf8HostToAscii() -> String {
if isValidUnicodeScala(self) {
return self
}
var labels = self.componentsSeparatedByString(".")
for (i, part) in labels.enumerate() {
if !isValidUnicodeScala(part) {
let a = encode(part)
labels[i] = prefixPunycode + a
}
}
let resultString = labels.joinWithSeparator(".")
return resultString
}
public func asciiHostToUTF8() -> String {
var labels = self.componentsSeparatedByString(".")
for (index, part) in labels.enumerate() {
if isValidPunycodeScala(part) {
let changeStr = part.substringFromIndex(part.startIndex.advancedBy(4))
labels[index] = decode(changeStr)
}
}
let resultString = labels.joinWithSeparator(".")
return resultString
}
}
@@ -232,7 +232,11 @@ class TabLocationView: UIView {
urlTextField.attributedText = attributedString
} else {
// If we're unable to highlight the domain, just use the URL as is.
urlTextField.text = url?.absoluteString
if let host = url?.host {
urlTextField.text = url?.absoluteString.stringByReplacingOccurrencesOfString(host, withString: host.asciiHostToUTF8())
} else {
urlTextField.text = url?.absoluteString
}
}
}
}
@@ -7,14 +7,16 @@ import Foundation
class URIFixup {
static func getURL(entry: String) -> NSURL? {
let trimmed = entry.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
let escaped = trimmed.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLAllowedCharacterSet())
guard let escaped = trimmed.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLAllowedCharacterSet()) else {
return nil
}
// Then check if the URL includes a scheme. This will handle
// all valid requests starting with "http://", "about:", etc.
// However, we ensure that the scheme is one that is listed in
// the official URI scheme list, so that other such search phrases
// like "filetype:" are recognised as searches rather than URLs.
if let escaped = escaped, url = NSURL(string: escaped) where url.schemeIsValid {
if let url = punycodedURL(escaped) where url.schemeIsValid {
return url
}
@@ -28,10 +30,16 @@ class URIFixup {
// If there is a ".", prepend "http://" and try again. Since this
// is strictly an "http://" URL, we also require a host.
if let url = NSURL(string: "http://\(trimmed)") where url.host != nil {
if let url = punycodedURL("http://\(escaped)") where url.host != nil {
return url
}
return nil
}
}
static func punycodedURL(string: String) -> NSURL? {
let components = NSURLComponents(string: string)
components?.host = components?.host?.utf8HostToAscii()
return components?.URL
}
}
@@ -686,7 +686,10 @@ extension URLBarView: TabLocationViewDelegate {
}
func tabLocationViewDidTapLocation(tabLocationView: TabLocationView) {
let locationText = delegate?.urlBarDisplayTextForURL(locationView.url)
var locationText = delegate?.urlBarDisplayTextForURL(locationView.url)
if let host = locationView.url?.host {
locationText = locationView.url?.absoluteString.stringByReplacingOccurrencesOfString(host, withString: host.asciiHostToUTF8())
}
enterOverlayMode(locationText, pasted: false)
}

0 comments on commit 78be545

Please sign in to comment.