  changeset:   312063:88bea96c802a
  user:        Andrea Marchesini <amarchesini@mozilla.com>
  Date:        Tue May 10 10:52:19 2016 +0200
  summary:     Bug 1267130 - Improve the URL segment calculation, r=valentin a=ritu

diff -r 28dcecced055 -r 88bea96c802a netwerk/base/nsStandardURL.cpp
--- a/netwerk/base/nsStandardURL.cpp	Wed May 18 11:55:29 2016 +1200
+++ b/netwerk/base/nsStandardURL.cpp	Tue May 10 10:52:19 2016 +0200
@@ -475,19 +475,28 @@
 }
 
 uint32_t
-nsStandardURL::AppendSegmentToBuf(char *buf, uint32_t i, const char *str, URLSegment &seg, const nsCString *escapedStr, bool useEscaped)
+nsStandardURL::AppendSegmentToBuf(char *buf, uint32_t i, const char *str,
+                                  const URLSegment &segInput, URLSegment &segOutput,
+                                  const nsCString *escapedStr,
+                                  bool useEscaped, int32_t *diff)
 {
-    if (seg.mLen > 0) {
+    MOZ_ASSERT(segInput.mLen == segOutput.mLen);
+
+    if (diff) *diff = 0;
+
+    if (segInput.mLen > 0) {
         if (useEscaped) {
-            seg.mLen = escapedStr->Length();
-            memcpy(buf + i, escapedStr->get(), seg.mLen);
+            MOZ_ASSERT(diff);
+            segOutput.mLen = escapedStr->Length();
+            *diff = segOutput.mLen - segInput.mLen;
+            memcpy(buf + i, escapedStr->get(), segOutput.mLen);
+        } else {
+            memcpy(buf + i, str + segInput.mPos, segInput.mLen);
         }
-        else
-            memcpy(buf + i, str + seg.mPos, seg.mLen);
-        seg.mPos = i;
-        i += seg.mLen;
+        segOutput.mPos = i;
+        i += segOutput.mLen;
     } else {
-        seg.mPos = i;
+        segOutput.mPos = i;
     }
     return i;
 }
@@ -598,6 +607,20 @@
         }
     }
 
+    // We must take a copy of every single segment because they are pointing to
+    // the |spec| while we are changing their value, in case we must use
+    // encoded strings.
+    URLSegment username(mUsername);
+    URLSegment password(mPassword);
+    URLSegment host(mHost);
+    URLSegment path(mPath);
+    URLSegment filepath(mFilepath);
+    URLSegment directory(mDirectory);
+    URLSegment basename(mBasename);
+    URLSegment extension(mExtension);
+    URLSegment query(mQuery);
+    URLSegment ref(mRef);
+
     //
     // generate the normalized URL string
     //
@@ -607,9 +630,10 @@
     char *buf;
     mSpec.BeginWriting(buf);
     uint32_t i = 0;
+    int32_t diff = 0;
 
     if (mScheme.mLen > 0) {
-        i = AppendSegmentToBuf(buf, i, spec, mScheme);
+        i = AppendSegmentToBuf(buf, i, spec, mScheme, mScheme);
         net_ToLowerCase(buf + mScheme.mPos, mScheme.mLen);
         i = AppendToBuf(buf, i, "://", 3);
     }
@@ -619,15 +643,22 @@
 
     // append authority
     if (mUsername.mLen > 0) {
-        i = AppendSegmentToBuf(buf, i, spec, mUsername, &encUsername, useEncUsername);
-        if (mPassword.mLen >= 0) {
+        i = AppendSegmentToBuf(buf, i, spec, username, mUsername,
+                               &encUsername, useEncUsername, &diff);
+        ShiftFromPassword(diff);
+        if (password.mLen >= 0) {
             buf[i++] = ':';
-            i = AppendSegmentToBuf(buf, i, spec, mPassword, &encPassword, useEncPassword);
+            i = AppendSegmentToBuf(buf, i, spec, password, mPassword,
+                                   &encPassword, useEncPassword, &diff);
+            ShiftFromHost(diff);
         }
         buf[i++] = '@';
     }
-    if (mHost.mLen > 0) {
-        i = AppendSegmentToBuf(buf, i, spec, mHost, &encHost, useEncHost);
+    if (host.mLen > 0) {
+        i = AppendSegmentToBuf(buf, i, spec, host, mHost, &encHost, useEncHost,
+                               &diff);
+        ShiftFromPath(diff);
+
         net_ToLowerCase(buf + mHost.mPos, mHost.mLen);
         MOZ_ASSERT(mPort >= -1, "Invalid negative mPort");
         if (mPort != -1 && mPort != mDefaultPort) {
@@ -652,21 +683,23 @@
     }
     else {
         uint32_t leadingSlash = 0;
-        if (spec[mPath.mPos] != '/') {
+        if (spec[path.mPos] != '/') {
             LOG(("adding leading slash to path\n"));
             leadingSlash = 1;
             buf[i++] = '/';
             // basename must exist, even if empty (bugs 113508, 429347)
             if (mBasename.mLen == -1) {
-                mBasename.mPos = i;
-                mBasename.mLen = 0;
+                mBasename.mPos = basename.mPos = i;
+                mBasename.mLen = basename.mLen = 0;
             }
         }
 
         // record corrected (file)path starting position
         mPath.mPos = mFilepath.mPos = i - leadingSlash;
 
-        i = AppendSegmentToBuf(buf, i, spec, mDirectory, &encDirectory, useEncDirectory);
+        i = AppendSegmentToBuf(buf, i, spec, directory, mDirectory,
+                               &encDirectory, useEncDirectory, &diff);
+        ShiftFromBasename(diff);
 
         // the directory must end with a '/'
         if (buf[i-1] != '/') {
@@ -674,7 +707,9 @@
             mDirectory.mLen++;
         }
 
-        i = AppendSegmentToBuf(buf, i, spec, mBasename, &encBasename, useEncBasename);
+        i = AppendSegmentToBuf(buf, i, spec, basename, mBasename,
+                               &encBasename, useEncBasename, &diff);
+        ShiftFromExtension(diff);
 
         // make corrections to directory segment if leadingSlash
         if (leadingSlash) {
@@ -687,18 +722,24 @@
 
         if (mExtension.mLen >= 0) {
             buf[i++] = '.';
-            i = AppendSegmentToBuf(buf, i, spec, mExtension, &encExtension, useEncExtension);
+            i = AppendSegmentToBuf(buf, i, spec, extension, mExtension,
+                                   &encExtension, useEncExtension, &diff);
+            ShiftFromQuery(diff);
         }
         // calculate corrected filepath length
         mFilepath.mLen = i - mFilepath.mPos;
 
         if (mQuery.mLen >= 0) {
             buf[i++] = '?';
-            i = AppendSegmentToBuf(buf, i, spec, mQuery, &encQuery, useEncQuery);
+            i = AppendSegmentToBuf(buf, i, spec, query, mQuery,
+                                   &encQuery, useEncQuery,
+                                   &diff);
+            ShiftFromRef(diff);
         }
         if (mRef.mLen >= 0) {
             buf[i++] = '#';
-            i = AppendSegmentToBuf(buf, i, spec, mRef, &encRef, useEncRef);
+            i = AppendSegmentToBuf(buf, i, spec, ref, mRef, &encRef, useEncRef,
+                                   &diff);
         }
         // calculate corrected path length
         mPath.mLen = i - mPath.mPos;
@@ -953,6 +994,39 @@
 #undef GOT_PREF
 }
 
+#define SHIFT_FROM(name, what)                    \
+void                                              \
+nsStandardURL::name(int32_t diff)                 \
+{                                                 \
+    if (!diff) return;                            \
+    if (what.mLen >= 0) {                         \
+        CheckedInt<int32_t> pos = what.mPos;      \
+        pos += diff;                              \
+        MOZ_ASSERT(pos.isValid());                \
+        what.mPos = pos.value();                  \
+    }
+
+#define SHIFT_FROM_NEXT(name, what, next)         \
+    SHIFT_FROM(name, what)                        \
+    next(diff);                                   \
+}
+
+#define SHIFT_FROM_LAST(name, what)               \
+    SHIFT_FROM(name, what)                        \
+}
+
+SHIFT_FROM_NEXT(ShiftFromAuthority, mAuthority, ShiftFromUsername)
+SHIFT_FROM_NEXT(ShiftFromUsername, mUsername, ShiftFromPassword)
+SHIFT_FROM_NEXT(ShiftFromPassword, mPassword, ShiftFromHost)
+SHIFT_FROM_NEXT(ShiftFromHost, mHost, ShiftFromPath)
+SHIFT_FROM_NEXT(ShiftFromPath, mPath, ShiftFromFilepath)
+SHIFT_FROM_NEXT(ShiftFromFilepath, mFilepath, ShiftFromDirectory)
+SHIFT_FROM_NEXT(ShiftFromDirectory, mDirectory, ShiftFromBasename)
+SHIFT_FROM_NEXT(ShiftFromBasename, mBasename, ShiftFromExtension)
+SHIFT_FROM_NEXT(ShiftFromExtension, mExtension, ShiftFromQuery)
+SHIFT_FROM_NEXT(ShiftFromQuery, mQuery, ShiftFromRef)
+SHIFT_FROM_LAST(ShiftFromRef, mRef)
+
 //----------------------------------------------------------------------------
 // nsStandardURL::nsISupports
 //----------------------------------------------------------------------------
diff -r 28dcecced055 -r 88bea96c802a netwerk/base/nsStandardURL.h
--- a/netwerk/base/nsStandardURL.h	Wed May 18 11:55:29 2016 +1200
+++ b/netwerk/base/nsStandardURL.h	Tue May 10 10:52:19 2016 +0200
@@ -77,6 +77,7 @@
 
         URLSegment() : mPos(0), mLen(-1) {}
         URLSegment(uint32_t pos, int32_t len) : mPos(pos), mLen(len) {}
+        URLSegment(const URLSegment& aCopy) : mPos(aCopy.mPos), mLen(aCopy.mLen) {}
         void Reset() { mPos = 0; mLen = -1; }
         // Merge another segment following this one to it if they're contiguous
         // Assumes we have something like "foo;bar" where this object is 'foo' and right
@@ -177,7 +178,10 @@
     bool     NormalizeIDN(const nsCSubstring &host, nsCString &result);
     void     CoalescePath(netCoalesceFlags coalesceFlag, char *path);
 
-    uint32_t AppendSegmentToBuf(char *, uint32_t, const char *, URLSegment &, const nsCString *esc=nullptr, bool useEsc = false);
+    uint32_t AppendSegmentToBuf(char *, uint32_t, const char *,
+                                const URLSegment &input, URLSegment &output,
+                                const nsCString *esc=nullptr,
+                                bool useEsc = false, int32_t* diff = nullptr);
     uint32_t AppendToBuf(char *, uint32_t, const char *, uint32_t);
 
     nsresult BuildNormalizedSpec(const char *spec);
@@ -216,17 +220,17 @@
     const nsDependentCSubstring Ref()       { return Segment(mRef); }
 
     // shift the URLSegments to the right by diff
-    void ShiftFromAuthority(int32_t diff) { mAuthority.mPos += diff; ShiftFromUsername(diff); }
-    void ShiftFromUsername(int32_t diff)  { mUsername.mPos += diff; ShiftFromPassword(diff); }
-    void ShiftFromPassword(int32_t diff)  { mPassword.mPos += diff; ShiftFromHost(diff); }
-    void ShiftFromHost(int32_t diff)      { mHost.mPos += diff; ShiftFromPath(diff); }
-    void ShiftFromPath(int32_t diff)      { mPath.mPos += diff; ShiftFromFilepath(diff); }
-    void ShiftFromFilepath(int32_t diff)  { mFilepath.mPos += diff; ShiftFromDirectory(diff); }
-    void ShiftFromDirectory(int32_t diff) { mDirectory.mPos += diff; ShiftFromBasename(diff); }
-    void ShiftFromBasename(int32_t diff)  { mBasename.mPos += diff; ShiftFromExtension(diff); }
-    void ShiftFromExtension(int32_t diff) { mExtension.mPos += diff; ShiftFromQuery(diff); }
-    void ShiftFromQuery(int32_t diff)     { mQuery.mPos += diff; ShiftFromRef(diff); }
-    void ShiftFromRef(int32_t diff)       { mRef.mPos += diff; }
+    void ShiftFromAuthority(int32_t diff);
+    void ShiftFromUsername(int32_t diff);
+    void ShiftFromPassword(int32_t diff);
+    void ShiftFromHost(int32_t diff);
+    void ShiftFromPath(int32_t diff);
+    void ShiftFromFilepath(int32_t diff);
+    void ShiftFromDirectory(int32_t diff);
+    void ShiftFromBasename(int32_t diff);
+    void ShiftFromExtension(int32_t diff);
+    void ShiftFromQuery(int32_t diff);
+    void ShiftFromRef(int32_t diff);
 
     // fastload helper functions
     nsresult ReadSegment(nsIBinaryInputStream *, URLSegment &);
